A Client Portal for Professional Services: What To Show, and What To Never Show
The hard part of a client portal is not what it shows. It is being certain, on every screen and every query, about what it does not.
A client portal looks like a presentation problem and is really an authorisation problem. The moment a firm exposes its delivery system to the people it is delivering to, every record in that system has to answer a question it never had to answer before: may this particular outsider see this?
Get it wrong in the generous direction and a client reads an internal risk entry describing their own sponsor as the project risk. There is no version of that conversation that ends well.
A filter is not a boundary
The tempting design is a flag on each record and a where clause on each query. It works, until somebody adds the fortieth query and forgets. The failure is silent: nothing errors, a screen simply shows one row too many.
The stronger design is a separate read path. Atlas gives the portal its own controllers, its own request shapes and its own read repository, and that repository never imports the internal query builders. There is no code path from a client session to the internal ones, which is a property somebody can check rather than a habit somebody has to keep.
Everything is internal until somebody says otherwise
Every exposable object defaults to internal. The only way something becomes client-visible is an explicit act that writes an audit row and a timeline entry, and that act cannot be performed through a generic update: an audience field settable in a normal edit form will eventually be set by accident.
The default is the whole safeguard. A system where objects start visible and get hidden leaks by omission; one where they start hidden and get shared leaks only by decision.
Scope a client to their own part
A client sponsor on a programme with four workstreams may be entitled to one of them. Portal access therefore needs a workstream scope as well as an engagement, and an empty scope has to mean the whole engagement rather than nothing, or every existing seat breaks the day the column ships.
What a client should be able to do, not just read
A read-only portal gets opened twice and then ignored. The portal earns its place when the client can complete something in it: sign off a deliverable, approve minutes, answer an information request, reply to a thread, upload the document the firm has been chasing.
Each of those is a write from an outsider, so each needs its own rate limit, its own audit row, and its own notification back to the firm side. In Atlas the portal mutations carry all three, and the download path is a signed, expiring URL rather than a permanent one.
The invitation is part of the security model
Portal access usually begins with an emailed link, which makes the invitation a credential. It should default to the narrowest role rather than the most useful one, expire, be revocable, and be rate limited on acceptance, because an unthrottled acceptance endpoint is a free guessing oracle against whatever token space it uses.