If your agency runs connectors for many clients, give each client its own workspace. A workspace has owner, member and viewer roles, per-project access overrides, invites by GitHub login, an audit log of every change, and usage metering per project. That layout keeps one client's projects, people, and costs separate from another's, so access and billing both have a clear answer per client instead of a shared pile.
Why one workspace per client?
Because a workspace is the boundary where access, audit, and metering all line up. Put every client in a shared space and you are constantly reasoning about which project belongs to whom. Give each client a workspace and the boundary does that work for you: your team operates inside, the client sees only their own, and the audit log and per-project metering are already scoped to that one client.
Who gets which role?
Every person in a workspace has one base role, and the split follows risk, not title:
- Viewer: read-only. Sees projects, deploy history, status and logs, with no start, stop, rollback or deploy controls. Right for the client stakeholder watching their connectors.
- Member: operates projects. Can deploy, start, stop, and roll back. Right for the agency engineers doing the daily work.
- Owner: everything a member can do, plus the sensitive surface, environment variables, custom domains, public paths, the egress allowlist, project access, deletion, and workspace membership. A workspace always keeps at least one owner.
A workable default: your agency leads are owners, your engineers are members, and the client's people are viewers on their own workspace.
How do I give a client access to one project only?
With a per-project override. Base roles set the default; a project's Access panel sets the exception. For any member, an owner can change the effective role on a single project, promote them to owner of one service, drop them to viewer on a sensitive one, or set the role to none, which hides the project entirely. A reset removes the override and the person falls back to their workspace role.
That is how you handle a client contractor who should touch exactly one connector: leave their base role low, override up on the one project. You do not spin up another workspace for a single exception.
How do clients and staff get added?
By GitHub login, the same identity they already use to push code. An owner invites octocat with a role, and the invite waits. When that person signs in to DenkOps with GitHub, the membership applies, with no email round-trip and no shared credentials. Every invite, and every later role change, lands in the audit log, so "who can reach this client's connectors" is a question you can answer with a record, not a memory.
How do I show each client their cost?
Usage is metered per project, requests and RAM-hours, so a client whose connectors run in their own workspace has a cost you can read off directly rather than estimate. When a client asks why this month is higher, you point at the project that drove it. Pair that with the audit log's deploys and rollbacks and you can tie a usage change to the specific version that caused it.
What about the connectors themselves?
Each connector is still a normal DenkOps project. Turn on the hosted OAuth flow so the client's users install it by pasting one URL and signing in, no auth code on your side. Keep auth by default on internal endpoints, and put each client's API keys in write-only secrets so one client's credentials never leak into another's project. If you are new to the model, start with what an MCP connector is and connectors for your SaaS. See security, features and pricing for the rest.