Notification Feed
The notification feed is the bell in the Console header. It answers one question — what happened while I was away? — for the infrastructure you can already see.
It is an activity feed, not an inbox and not a pager. A notification is a record that something happened at a moment in time. It is never updated, never re-derived, and never withdrawn: if a compliance score drops and then recovers, the feed carries two events, not one changing one.
The bell and the on-call paging chain are different subsystems that share a word. Nothing in the feed wakes anybody up, sends an SMS, places a voice call, opens an incident, or starts an escalation timer. Turning the feed on, off, or down cannot suppress a page, and it cannot cause one.
If you need to be woken up about something, that is alerting and on-call — a different surface with different guarantees. If you need to find out about it next time you open Console, that is this.
What you see
Every event carries the same five things and nothing else:
| Field | What it is |
|---|---|
| Category | A short label above the title — "Version currency", "Compliance". Drawn from the event's kind. |
| Title | One line, written by whatever produced the event. |
| Body | Optional detail under the title. |
| Link | Optional. Takes you to the page that owns the fact. Only in-app links are followed. |
| Time | When it happened. |
There is no severity, no priority, no colour-coding and no ordering by importance. Events are newest-first and that is the only order there is. See why.
The panel shows the 20 most recent events you can see. There is no "load more" and no page count: a bell is not a table, and anything you want to search or filter lives on the page that owns it.
Where the bell appears
In the header of the main application, the admin area (/admin/*), and the on-call workspace (/oncall/*, /alerts). It never renders while signed out.
It refreshes once a minute while its tab is focused, and not at all in a background tab. Clearing the badge does not wait for that interval — opening the panel marks it read immediately.
Reading is one moment, not a checklist
Your read state is a single timestamp: the last moment you looked. An event is unread if it happened after that moment.
Opening the panel advances that timestamp to now — and it advances again if new events arrive while the panel is still open, because the rule is "while the panel is open, what is in it has been read", not "the click marked things read".
That is the whole read model. There is:
- no per-item unread state,
- no dismiss, no "mark this one read", no archive,
- no way to mark something unread again.
Why there is no per-item state, since it is the first thing people ask for. A per-item done flag is the affordance for "I have handled this" — and the feed has no idea whether anything was done. It is not connected to the compliance run, the CVE, or the rollout it is telling you about; it cannot verify a claim of completion, cannot re-open one when the condition comes back, and cannot show anyone else that you took it. Offering the checkbox would promise a workflow the feed cannot back up, and the reliable-looking half of that promise is the dangerous one. The surfaces that do own the work — alerts, compliance, the versions page — have their own state, and that state is the truth.
One consequence worth knowing: the watermark is a moment across your whole feed, not one per client. It is also not retroactive — if you are granted access to a new project tomorrow, its older events appear in your list but do not light the badge, because they happened before you last looked.
Who sees what
Visibility is worked out when you read, from the grants you hold at that moment. Nothing is stored on the event about who its audience is — an audience written when the event was published would go stale the moment a host was re-homed or a role was changed.
Each event carries a scope pair: a project (client_id) and, optionally, an environment (environment_id).
| The event is about | Who sees it |
|---|---|
| One environment of a project | People with a grant on that environment, and anyone with a project-wide grant. Someone scoped to a different environment of the same project does not see it. |
| A whole project (no environment) | Everyone with a grant anywhere in that project, environment-scoped people included. Nobody outside it. |
| Nothing in particular (no project) | Super admins only. These are platform-level events — a global catalog sync failing, say. |
Those three are the only shapes there are, and the database enforces it: an event pinned to an environment but naming no project is rejected outright, because it would be unreachable by everyone except a super admin — which is never what its producer meant. See the producer contract.
The feed reads under the same permission as the host inventory (hosts:read), because everything it is allowed to carry is about infrastructure you can already open. It has no permission of its own to grant or revoke.
A reader who holds nothing anywhere gets an empty feed, not an error. There is no "access denied" state on the bell: being refused by something you were never offered is a worse answer than an empty panel. The same is true for a super admin whose access has been narrowed to specific projects — they read the feed as an ordinary member of those projects, and platform-level events (the ones with no project at all) are not among them.
The three empty-looking states
The panel distinguishes three things that all look like an absence of news, because conflating them is how a broken feed comes to look healthy:
- "Nothing has happened yet" — the feed loaded, and there is nothing in it. On a fresh install this is the correct and expected state; see below.
- "Notifications could not be loaded" — the feed could not be read at all, with a Retry button. This is a statement about Console, not about your infrastructure.
- "Could not refresh — showing the last events received" — a refresh failed but earlier events are still on screen. They are kept rather than replaced with an error, and the panel says so rather than presenting a feed that stopped updating twenty minutes ago as current.
A failed refresh retries on its own; there is nothing to click.
How long events are kept
Events are deleted 30 days after they happen, by a background sweep. The window is PROXIMA_NOTIFICATION_RETENTION_DAYS (see Environment Variables); setting it to zero or a negative number disables the sweep entirely, and the table then grows without bound.
Deleting an event deletes nothing else. The feed is a notice about a fact, never the record of it — so if you want anything older than the window, go to the surface that owns it:
| Looking for | Go to |
|---|---|
| End-of-life software, CVEs, version posture | the Versions page |
| An alert, its acknowledgement or its resolution | Alerts and the on-call surfaces |
| A configuration change, and what changed | the Changes timeline |
| Compliance results and their history | Compliance |
| Who did what, and when | the audit log |
The feed ships empty
Nothing publishes into the feed yet. On every install today the bell opens on "Nothing has happened yet", and that is the honest state rather than a fault — the first producer (version currency) is a separate piece of work.
If you are writing that producer, or any producer: read the producer contract first. It is a short document and one of its rules is a security rule.