feat(tenant): per-user tenant identity for a shared team backend (spec 21) - #113
Merged
Conversation
…c 21)
Second spec-21 foundation (after the DistLock): the per-user isolation basis
(Q-REMOTE-TENANT). On a shared team cluster the naive "name the role after the
project" is NOT isolation — two developers' `app` projects collide and can read
each other's data. internal/tenant resolves a per-developer identity that every
provisioner namespaces resources by.
- Identity{Name}: empty on a LOCAL backend (Qualify is a no-op → names stay
<project>, fully backward compatible), a sanitized per-user name on a REMOTE
team backend.
- Resolve(remote, configured, deps): local → empty; remote precedence is
DEVSTACK_TENANT env → the workspace-configured identity → the OS username →
"user" (a nameless account still isolates). Deps injects env + os-user lookups
so it is unit-testable without touching the real environment.
- Sanitize: lowercases, collapses non-alphanumeric runs to a single hyphen, trims
and length-caps — a fragment valid across the strictest engine namespace (S3
buckets) and STABLE (the same user maps to the same tenant every run/machine).
- Qualify(name, sep): engine-agnostic namespacing (role `u`+`_`, db `_`, bucket
`-`) — each provisioner keeps ownership of its identifier map (spec 27).
Unit-tested: local no-op, remote precedence, junk/nameless fallbacks, sanitize
edge cases (unicode, symbols, length), and both separators. Not yet wired into
the provisioners — that lands with the remote-ledger + tenant-column integration,
validated on the owner's VPS per the release-quality bar.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Second spec-21 foundation (after the DistLock in #112): the per-user isolation basis (Q-REMOTE-TENANT).
Why
On a shared team cluster, "name the role after the project" is not isolation — two developers both running a project named
appcollide and can read each other's data. Every provisioned resource must be namespaced by the developer's tenant identity (roleu_<user>_<project>, db<user>_<project>, bucket<user>-<project>-…).What
internal/tenant— resolve + sanitize the identity; provisioners compose the namespaced name with their own separator (spec 27: a provisioner owns its identifier map).Identity{Name}— empty on a LOCAL backend (Qualifyis a no-op → names stay<project>, fully backward compatible), a sanitized per-user name on a remote team backend.Resolve(remote, configured, deps)— local → empty; remote precedence:DEVSTACK_TENANTenv → workspace-configured identity → OS username →"user".Depsinjects env + os-user lookups for testability.Sanitize— lowercase, collapse non-alphanumeric runs to a hyphen, trim, length-cap → valid across the strictest engine namespace (S3 buckets) and stable (same user → same tenant every run/machine).Qualify(name, sep)— engine-agnostic namespacing.Tests
Local no-op, remote precedence, junk/nameless fallbacks, sanitize edge cases (unicode, symbols, length cap), both separators. Not yet wired into the provisioners — that lands with the remote-ledger +
provisionedtenant-column integration, validated on the VPS per the release-quality bar.Stacks conceptually on #112 (independent code; both are spec-21 foundations).
🤖 Generated with Claude Code