A secure platform for formal correspondence in a large organization: drafting, approval, dispatch, notes, tasks and search.
Less paper
Formal correspondence moves into one platform, reducing paper-based correspondence.
Every letter searchable
Full-text search across all correspondence makes any letter easy to find.
A modern experience
A new interface in place of the old system’s dated one.
Drafting with the AI Copilot
Helps with official correspondence and reduces the time it takes.
Services that scale on their own
Independent services over a shared database, so each component scales individually.
- Core
- Mail and notes
- Search
- Chat
- Support
- Tasks
- Files
- Integration
- Copilot
- Job engine
- Doc renderer
- Templating
Client
- Next.js
- React
- TypeScript
Services
- Node.js
- TypeScript
- GraphQL
Cache and queue
- Redis
- RabbitMQ
Data and search
- SQL database
- Elasticsearch
- Object storage
Identity
- OIDC provider
- LDAP directory
AI and documents
- LLM and OCR
- Word to PDF rendering
- Template rendering
Step through a request one hop at a time, or switch to Trace and break to see which flows a service takes part in.
The whole system. Pick a scenario, then step through it. The view zooms to the services each step involves.
- Request
- Search
- Events
- Sessions
Server-side sessions instead of tokens in the browser
Chose
A backend-for-frontend signs the user in and keeps the session on the server, in the database and then the cache. The browser holds only a session cookie.
Rejected
Keeping tokens in the browser.
Why
What a person can see depends on the role they sign in under, for example executive or non-executive, and the identity provider cannot supply that. People can also switch roles mid-session, which a server-side session can follow. Tokens held in a browser can be stolen.
- Messaging
Events instead of direct calls
Chose
Dispatching a letter, and tagging, publish events to RabbitMQ. Search indexing, notifications and document tracking consume them afterward.
Rejected
The mail service calling each of those services directly.
Why
Dispatching does not wait on indexing, notifying or tracking, and each event waits in the queue until its consumer has processed it.
- API layer
One generic CRUD factory with authorization built in
Chose
A generic GraphQL factory that generates the endpoints for an entity, with the authorization setup included.
Rejected
Writing list queries and authorization separately for each entity and service.
Why
The platform also needed a user-management module where admins read and write records. One factory gave many endpoints out of the box and removed the same list queries being written again and again.