Sodium
A multi-tenant realtime workspace for projects, chat, files, and calendars, with one path from a change to every open screen.
A workspace for each organization
Sodium is a workspace for projects, tasks, chat, files, notifications, and calendars. Each organization is a realm. Records of which workspaces exist, and who belongs to them, stay apart from the projects, messages, and files inside a realm. That realm data lives in its own PostgreSQL schema, and Keycloak issues identity for the realm.
People reach a workspace on its own subdomain. Ingress terminates TLS and routes the browser to the web app, the API, or authentication. Local development runs through Docker Compose. Production runs the API and the web app as two replicas each on Kubernetes, with Redis carrying events between them.

One route from the server to the screen
A durable change follows one sequence. The person who makes it sees an optimistic update immediately. The API writes the realm-scoped record, then publishes one domain event. A listener broadcasts that event over Socket.IO into a room matched to who can see the work. The browser receives backend events in one place, and a projection writes them into the query cache. Every connected client, including the one that started the change, settles on the same server state.
Typing, presence, and drag previews travel on a separate lane. They coordinate the interface inside an authorized room. The workspace record stays on the durable path.

Private work stays in private rooms
A private project, calendar, or personal item is broadcast only to that project, that person, or that calendar. Workspace-visible entities can use the realm room. Mutations check a shared permission catalog on the server, and the interface gates the same actions.
A CI gate keeps this shape in order. It checks that a domain event has a listener, that the listener reaches the client, that the client routes the event into a projection, and that a private entity is not sent to the whole realm. The gate runs twenty-one ordered checks and stops at the first failure.

The clock depends on whose day it is
Timestamps are stored in UTC. A dashboard, an analytics window, or a shared report cuts days by the realm timezone. A personal agenda or quiet hours uses that person's timezone. Formatting a time for the screen does not change which day the organization counts.
A task's column is where the card sits. Its workflow category and workflow state carry what the work means. Filters and summaries use that meaning, so moving a card between columns does not rewrite its lifecycle.

Outside tools join the same record
GitHub, Slack, and WordPress arrive through an ingestion gateway. The gateway accepts a signal once the actor and the realm are known, then stores it as a workspace event. Automation, through n8n, sits in front of that gateway. Sodium keeps the meaning: when the event happened, when it was received, and how it links into the project graph.
That same route, from a mutation through an event and a broadcast into a projection, is what RiverGen packages for other realtime applications.

