Tenant/Repo/App Surface Implementation Plan
Tenant/Repo/App Surface Implementation Plan
Goal
Implement the new domain direction:
Tenant -> Config Repository -> App Surface- Keep Git lifecycle repo-scoped
(
workspace -> changeset -> release -> deploy) - Add multi-surface support for URLs/runtime profile context
This plan is optimized for current reality: pre-production and one active user.
What changes
- Current
Appbecomes a repository concept in practice. - Add first-class
Tenant. - Add first-class
App Surfaceunder each repository. - Keep existing
/api/appsworking (backward-compatible) while adding clearer/api/reposand/api/tenantsAPIs. - Runtime profiles gain surface endpoint mapping.
What stays the same
- Queue-first changesets/releases model.
- One integration branch per repository.
- Environment pipeline and deploy flow.
- gitaly-rs integration boundary.
Implementation order (critical path)
Step 1: Domain + storage model
Crates/files:
conman-core: addtenant.rs,app_surface.rs; keepapp.rsas repo model.conman-db: addtenant_repo.rs,app_surface_repo.rs.conman-db/src/lib.rs: export new repos and include index bootstrap.
Tasks:
- Add domain structs:
Tenant { id, name, slug, created_at, updated_at }AppSurface { id, repo_id, key, title, domains, branding?, roles?, created_at, updated_at }
- Extend existing app/repo model with
tenant_id. - Add Mongo collections/indexes:
tenants.sluguniqueapp_surfaces (repo_id, key)uniqueapps.tenant_idnon-unique index
- One-time local backfill:
- create a default tenant
- assign all existing repos to it
Done when:
- service boots with new collections/indexes
- existing app/repo flows still work
Step 2: API surface (tenant + repo + app-surface)
Crates/files:
conman-api/src/handlers/tenants.rs(new)conman-api/src/handlers/repos.rs(new alias/clear naming over current app handlers)conman-api/src/handlers/app_surfaces.rs(new)- router wiring in
conman-api
Tasks:
- Add tenant endpoints:
POST /api/tenantsGET /api/tenantsGET /api/tenants/:tenantId
- Add repo endpoints:
POST /api/tenants/:tenantId/reposGET /api/reposGET /api/repos/:repoId
- Keep
/api/appsendpoints functional as compatibility alias. - Add app-surface endpoints:
POST /api/repos/:repoId/surfacesGET /api/repos/:repoId/surfacesPATCH /api/repos/:repoId/surfaces/:surfaceId
Done when:
- tenant/repo/surface can be created and listed end-to-end
/api/appsexisting calls still pass manual smoke tests
Step 3: Runtime profile multi-surface support
Crates/files:
conman-core/src/runtime_profile.rsconman-db/src/runtime_profile_repo.rs- profile handlers under
conman-api
Tasks:
- Add
surface_endpointsto runtime profile:- shape:
HashMap<String, String>(surface_key -> base_url)
- shape:
- Validate keys:
- must match existing repo surface keys
- Keep existing single-url fields as compatibility path (if present).
- Update temp env derivation logic so endpoint overrides can be per-surface.
Done when:
- environment profile can define endpoints for multiple app surfaces
- temp env creation keeps endpoint map and applies overrides correctly
Step 4: Changeset/release visibility for surfaces
Crates/files:
conman-core/src/changeset.rsconman-core/src/release.rsconman-apichangeset/release handlers
Tasks:
- Add
impacted_surface_keys: Vec<String>to changeset metadata. - Populate it from:
- changed file paths (first pass)
- optional semantic diff enrichment (second pass)
- Propagate impact summary into release detail responses.
Done when:
- review and release screens can show which surfaces are affected
Step 5: Auth and membership alignment
Keep it simple for now:
- Membership remains repo-scoped (current behavior).
app_adminremains the admin capability role.- Tenant-level admin model can be deferred.
Tasks:
- Ensure new tenant/repo/surface endpoints enforce current RBAC correctly.
- Ensure invite/member flows still work with repo IDs unchanged.
Done when:
- no regression in login/invite/member assignment
Step 6: Test and docs pass
Tasks:
- Add/adjust unit and integration tests:
- tenant/surface repos
- new handlers
- runtime profile endpoint map validation
- Update manual test guide with new setup sequence:
- create tenant -> create repo -> create surfaces
- Keep OpenAPI docs aligned with the new endpoints.
Done when:
cargo test --workspacepasses- manual API sequence works cleanly with tenant/repo/surface model
Practical execution checklist
- Implement Step 1 and Step 2 first (unblocks everything else).
- Then Step 3 (runtime profiles).
- Then Step 4 (impacted surface metadata).
- Finish with Step 5 and Step 6 hardening.
Notes for this repo right now
- Existing code already treats
Applike a repo (repo_path,integration_branch), so this change is mostly additive + naming clarity. - Because this is pre-production and single-user, we can keep the backfill lightweight and local without rollout orchestration.
Automated acceptance criteria
When implementation is complete, these checks must pass.
| ID | Criteria | Automated check |
|---|---|---|
| TRS-AC-01 | Tenant can be created and queried. | run_tenant_repo_surface_acceptance.sh |
| TRS-AC-02 | Repository can be created under a tenant and queried via
/api/repos/:id. |
run_tenant_repo_surface_acceptance.sh |
| TRS-AC-03 | /api/apps/:id compatibility still works for repo
records. |
run_tenant_repo_surface_acceptance.sh |
| TRS-AC-04 | Two app surfaces can be created and listed for one repo. | run_tenant_repo_surface_acceptance.sh |
| TRS-AC-05 | Runtime profile stores and returns
surface_endpoints. |
run_tenant_repo_surface_acceptance.sh |
| TRS-AC-06 | Environment configuration can reference runtime profiles after model change. | run_tenant_repo_surface_acceptance.sh |
| TRS-AC-07 | Existing lifecycle smoke remains functional after model change. | tests/e2e/run_full_staged_smoke.sh |
Acceptance command set
CONMAN_BASE_URL=... CONMAN_LOGIN_EMAIL=... CONMAN_LOGIN_PASSWORD=... CONMAN_ACCEPTANCE_REPO_PATH=... ./tests/ops/run_tenant_repo_surface_acceptance.sh --strict./tests/e2e/run_full_staged_smoke.sh
The first command validates the new model contracts. The second command guards against regressions in the current app/workspace/changeset/release/deploy flow.