Skip to content

feat(tenancy): record the tenant in metrics and the console view - #24

Merged
blakebauman merged 1 commit into
mainfrom
feat/tenant-record
Aug 10, 2026
Merged

blakebauman merged 1 commit into
mainfrom
feat/tenant-record

Conversation

@blakebauman

Copy link
Copy Markdown
Member

Phase 5 of docs/design-tenancy.md. The audit field landed with resolution in phase 1; this adds the metric dimension and makes the console's federation view the viewer's.

The design said "as a metric label". It cannot be one.

The v1 contract freezes metric names and label sets, so a tenant label on fold_requests_total would break every dashboard and recording rule built on it — and that contract is one of the things the roadmap says vetoes a feature regardless of demand. New metric names are additive, which the contract permits, so the dimension ships as its own series:

  • fold_tenant_requests_total{tenant, outcome}
  • fold_tenant_upstream_calls_total{tenant} — the same unit a tenant budget is charged in, so an allowance can be watched being spent rather than reconstructed from the audit stream afterwards

Cardinality is the declared tenant count: config-bounded, exactly like the upstream label. Untenanted traffic records nothing rather than a tenant="" series — an empty label reads as a tenant in a dashboard, and those requests are already in fold_requests_total. TestFrozenMetricsGainNoTenantLabel asserts no frozen metric ever grows the label; it is what fails if someone later folds these two series into one.

The correction is recorded in the design record next to the earlier overlap one.

The console half is a boundary, not a nicety

The state API listed every upstream's URL, owner, and labels to any authenticated principal. After phase 4 that made the dashboard the one place a scoped tenant could read the topology its own traffic is refused. The federation view is now the viewer's — subset, counts, and the tenant's own limits, so a customer-facing console can answer "what am I allowed" without an operator relaying it.

docs/security-model.md stated the broader disclosure rule as unconditional; it now records the exception.

Verification

make check green, lint 0 issues, latency gate unmoved at 180 µs. Tests cover the two series counting requests and invocations, the outcome dimension on a denial, the frozen-label assertion, the no-empty-series rule, and the console view being tenant-scoped with counts that agree with the list.

Remaining tenancy work after this: phase 6, the docs pass (README config section, operations.md walkthrough, defaults.md, and the security-model.md rewrite of "Tenant isolation under load", which still describes the pre-tenancy emergent version).

🤖 Generated with Claude Code

Phase 5 of docs/design-tenancy.md. The audit field landed with resolution in
phase 1; this adds the metric dimension and makes the console's federation
view the viewer's.

The design said "as a metric label". It cannot be one, and the correction is
recorded in the design next to the overlap one. The v1 contract freezes metric
names *and label sets*, so a tenant label on fold_requests_total would break
every dashboard and recording rule built on it — and the contract is one of
the things the roadmap says vetoes a feature regardless of demand. New names
are additive, which the contract permits, so the dimension ships as its own
series: fold_tenant_requests_total{tenant,outcome} and
fold_tenant_upstream_calls_total{tenant}, the second counting the same unit a
tenant budget is charged in, so an allowance can be watched being spent rather
than reconstructed from the audit stream afterwards. Cardinality is the
declared tenant count — config-bounded, like the upstream label.

Untenanted traffic records nothing rather than a tenant="" series: an empty
label reads as a tenant in a dashboard, and those requests are already counted
in fold_requests_total. A test asserts no frozen metric ever grows the label,
which is what fails if someone later folds these two series into one.

The console half turned out to be a boundary, not a nicety. The state API
listed every upstream's URL, owner, and labels to any authenticated principal,
which after phase 4 made the dashboard the one place a scoped tenant could
read the topology its own traffic is refused. The federation view is now the
viewer's — subset, counts, and the tenant's own limits, so a customer-facing
console can answer "what am I allowed" without an operator relaying it.
security-model.md stated the broader disclosure rule as unconditional; it now
records the exception.

Latency gate unmoved at 180us.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@blakebauman
blakebauman merged commit 456618c into main Aug 10, 2026
6 checks passed
@blakebauman
blakebauman deleted the feat/tenant-record branch August 11, 2026 13:53
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