Skip to content

feat(tenant): per-user tenant identity for a shared team backend (spec 21) - #113

Merged
gustavobertoi merged 1 commit into
mainfrom
feat/remote-tenant
Jul 1, 2026
Merged

feat(tenant): per-user tenant identity for a shared team backend (spec 21)#113
gustavobertoi merged 1 commit into
mainfrom
feat/remote-tenant

Conversation

@gustavobertoi

Copy link
Copy Markdown
Contributor

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 app collide and can read each other's data. Every provisioned resource must be namespaced by the developer's tenant identity (role u_<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 (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: DEVSTACK_TENANT env → workspace-configured identity → OS username → "user". Deps injects 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 + provisioned tenant-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

…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>
@gustavobertoi
gustavobertoi merged commit c82cc69 into main Jul 1, 2026
4 checks passed
@gustavobertoi
gustavobertoi deleted the feat/remote-tenant branch July 1, 2026 22:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant