tenant-engine/SCOPE.md
tegwick ec41920be6 Refresh stale scope and architecture notes to match shipped state
SCOPE.md still said TEN-WP-0008 was "ready, not done" two paragraphs
above its own table listing staged promotion as shipped, and its list of
finished workplans omitted 0008 and 0011. All twelve workplans are
finished; say so once.

.claude/rules/architecture.md still described `guardrail/` as a reserved,
unimplemented namespace and the production store as TBD. Guardrails
shipped in TEN-WP-0006/0007 and PostgreSQL became the production store in
TEN-WP-0009. Also corrects the live-lookup caller to `access-engine`,
matching SCOPE.md and the boundary contract.

No behaviour change; 287 tests pass unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HHwvAEQfmzLHtrFGhXtVjq

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 823014@bnt-lap001
Assistant-Session: 2a0786b1-efea-4c38-959b-6e86a493f259
2026-09-07 00:21:03 +02:00

7 KiB

SCOPE

Lightweight boundary for agents and contributors. Full contract: net-kingdom/canon/standards/tenant-engine-boundary-contract_v0.1.md. Aspirational role: INTENT.md. Layer declaration: Engine / PIP (INTENT.md frontmatter; TEN-DEC-2026-001).


One-liner

Canonical PIP of tenant-as-an-entity facts for NetKingdom: existence, lifecycle, onboarding grouping, capability roles, plan/subscription assignment, and guardrail ceilings.


Core Idea

tenant-engine answers "what is this tenant, what roles does it hold, what plan is it on, and how far may it go" for two consumers: key-cape (cached, at token issuance) and access-engine / flex-auth (live, for high-stakes decisions). It never stores users, issues tokens, evaluates approvals, or makes authorization decisions. Tenancy context is an input to the decision, not a decision.


Layer

Key Value
Layer Engine
Role PIP
Statute net-kingdom/canon/standards/security-layer-model_v0.7.md
Companion net-kingdom/SECURITY-COMPANION.md
Catalog row tenant-as-an-entity facts (§4)
Tooling contacts none (key-cape and OpenBao are not clients of this repo)
Non-Tooling clients PostgreSQL / SQLite (own store); flex-auth POST /v1/check (Engine API on the write and authorized-read path); State Hub (work records, not a runtime dependency)
Write path PEP-shaped: fail-closed when access-engine is unset or unreachable
Conformance today Declaration is in our own voice. Mechanical layer.yaml check, persisted decision records, published stance map, and independent evidence custody are not yet shipped — see the review and TEN-WP-0011.

In Scope

  • Tenant records: identifier, lifecycle (active / retired), current onboarding grouping (ADR-0013). Grouping is mutable through an audited reclassification, not by renaming tenant_id.
  • Capability role grants/revocations: PLTF/IAM/VEN/CUS (ADR-0014), audited, mostly plan-linked.
  • Plan/subscription assignment, referenced by adaptive-pricing plan id.
  • Guardrail/quota policy: spend / entity-count / action-count ceilings, resolved as a total function of grouping, plan, override, and lifecycle. Seeded key: spend.monthly. trial defaults to zero.
  • Cache-read API for key-cape (token-issuance-time tenant_roles claim source): GET /tenants/{id}/roles.
  • Live-lookup API for access-engine (high-stakes, aal2-class decisions): GET /tenants/{id}/roles/live. Store unavailability returns 503, never 200 plus empty roles.
  • Write API for create / grant / revoke / plan / metadata / retire / reactivate / grouping / guardrail mutations, authorization-gated by flex-auth. Unset or unreachable authorizer denies.
  • Authoritative tenant read with ETag / If-Match / idempotency on lifecycle and guardrail mutations.

Out of Scope

  • Identity, authentication, MFA, token issuance — key-cape/Keycloak.
  • Authorization decisions — access-engine is the PDP; tenant-engine is a data source it queries, never the other way around.
  • Approval objects and approval lifecycle — approval-engine. Contested in TEN-DEC-2026-001; this repo will not grow one.
  • Users, profiles, memberships — user-engine. tenant_id is the only key shared between the two services.
  • Pricing-model / plan term definitions — adaptive-pricing.
  • Payment processing — not yet identified as owned by any repo.
  • Runtime secret custody — OpenBao / secrets-engine.
  • Metering / consumption counters — whoever meters the resource writes them; this service does not observe spend.
  • Rate limiting / traffic shaping — gateway concern, not action_count.
  • Containment / actuation — unowned and held at zero estate-wide.
  • Irreversible operational decisions without human approval.
  • Direct Tooling-layer clients — Staff and agents reach tenant facts through this Engine, not past it.

Current State

Production service, not a bootstrap. Workplans TEN-WP-0001 through TEN-WP-0011 are all finished: they shipped the domain model, the three boundary-contract APIs, the flex-auth write authorizer, lifecycle, guardrails, PostgreSQL as the production store, mutable grouping, staged-promotion onboarding, and the security-layer conformance surfaces. No workplan in this repo is currently open.

Surface Shipped Notes
Domain model + SQLite/in-memory store yes TEN-WP-0002
Cache-read / live-lookup / write APIs yes TEN-WP-0002
flex-auth WriteAuthorizer yes Fail-closed; is_allowed() returns a bool and discards the decision envelope
Production runtime yes TEN-WP-0004; image pin in deploy/base/tenant-engine.yaml
Lifecycle (update / retire / reactivate) yes TEN-WP-0005; no hard-delete
Guardrail policy yes TEN-WP-0006 / 0007; docs/tenant-guardrail-policy.md
PostgreSQL production store yes TEN-WP-0009; deploy mounts TENANT_ENGINE_DATABASE_URL_FILE
Mutable grouping yes TEN-WP-0010; identifier segment stays historical
Staged promotion yes (declared) TEN-WP-0008; railiance/app.toml; live pin recorded as previous_stable. Helm CLI apply is a railiance-bootstrap gap (RAIL-BS-IN-0001).
Machine-readable layer.yaml yes TEN-WP-0011-T01; check in scripts/check_layer_conformance.py
Persisted authorization_decision_id yes authz_records plus mutation event payload (TEN-WP-0011-T02)
Published unreachable-engine stance map yes pep-stance.yaml, fail-closed, tested equal to shipped behaviour
Independent audit-core emission path shipped, sender pending Local outbox + POST /v1/events; AUDIT-IN-0002
Bounded event-read interface yes events_for(tenant_id) only (TEN-WP-0011-T05)
Claim freshness / input-class lifetime yes pip-claims.yaml (TEN-WP-0011-T03)

The boundary contract still labels guardrail policy "reserved, not implemented" and still calls this repo "not a policy enforcement point". Both sentences are stale: guardrails shipped, and the write path is PEP-shaped even though this repo is not a PDP. Amendment is a net-kingdom change, requested from TEN-WP-0011.

Getting Oriented

  • Start with: INTENT.md
  • Working companion: net-kingdom/SECURITY-COMPANION.md
  • Statute: net-kingdom/canon/standards/security-layer-model_v0.7.md
  • Ownership contract: net-kingdom/canon/standards/tenant-engine-boundary-contract_v0.1.md
  • Claim/carrying mechanism: net-kingdom/canon/standards/iam-profile_v0.3.md
  • Layer declaration: decisions/decisions.md TEN-DEC-2026-001
  • Review: history/2026-08-29-security-layer-intent-scope-review.md
  • Evolution workplan: workplans/TEN-WP-0011-security-layer-conformance.md
  • flex-auth write-authorization integration: docs/flex-auth-integration.md
  • Guardrail contract: docs/tenant-guardrail-policy.md
  • Lifecycle contract: docs/tenant-lifecycle-api.md
  • Agent instructions: AGENTS.md, CLAUDE.md
  • Workplans: workplans/