Skip to content

Operator console Phase 0: warm SLO tiers, durable SLIs, named operators, honest copy - #692

Merged
crs48 merged 17 commits into
mainfrom
claude/0433-operator-console-the-decided-plan
Aug 2, 2026
Merged

Operator console Phase 0: warm SLO tiers, durable SLIs, named operators, honest copy#692
crs48 merged 17 commits into
mainfrom
claude/0433-operator-console-the-decided-plan

Conversation

@crs48

@crs48 crs48 commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Implements exploration 0433 — the ship-first defects and all of Phase 0. The console itself (Phases 1–3) is deliberately deferred; see below.

The plan and its research (0431) are in this PR too, along with ADR-31 and ADR-32.

Two defects that shipped first

D1 — the tiers that sell an SLO were provisioned scale-to-zero. minInstances keyed off isolation === 'dedicated-warm', which is team — best-effort, so it can never burn an error budget. The three plans that do publish an objective (community, company at 99.9%, enterprise at 99.95%) all scaled to zero, and region-pinned fell through to 0. A cold start can spend a large fraction of a 43-minute monthly budget.

Warmth is now a floor built from both the published objective and the isolation tier. Modelling the cost caught a regression in the first attempt: an objective-only rule would have dropped team to scale-to-zero, and PLAN_PRICING models it warm: true. Cost delta is ~$6/mo per SLO tenant, already assumed by the price list.

D2 — five surfaces claimed we cannot read data the hub indexes. search-indexer.ts extracts plaintext to build the FTS index, and 0343 records that the trusted tier gives integrity but not confidentiality. The repo already holds this standard — HonestyBox.astro and HonestMachine.astro explicitly refuse to overclaim E2EE — the cloud surface had drifted from it. Two of the five were found during implementation, not in the audit. privacy.astro is unchanged: "we cannot read your data with it" scopes to the billing identity and is accurate.

Phase 0

  • Durable bucketed SLIs. The in-memory ring held 2000 samples at 60s — 33 hours of a window labelled 30 days — and died with the process, so every deploy handed the error budget back at 100%. rollout/engine.ts gates fleet upgrades on that budget, so a restart silently unfroze deploys. Now one document per tenant per hour, rolled to daily at 30 days, pruned at 13 months.
  • windowState distinguishes stale from young. Stale (measurement broken) freezes; young (new tenant) is excluded. A gate that froze on every signup would get switched off.
  • unmeasured status. publicStatus hardcoded control-plane: operational — tautological, it said only that the process answered this request. The control plane now reports from leased-job staleness.
  • Named operators, fail-closed audit. Mutating internal routes require an operator identity plus a typed reason and reject the shared secret; the three read routes keep it, so cloud-company-metrics.mjs is unchanged. audited() writes the durable entry before the action runs. Tier 2 publishes signed nodes to the ops hub and queues visibly when unreachable.
  • Negative control. check:sli-gate --selftest plants five violations fleetGate MUST flag plus three it must not, and joins the existing check:gate-controls. Verified it goes red: removing the stale check fails the control, restoring it passes.

What is NOT here

Phases 1–3 (the React console, support view, consent flow). Two reasons, neither of them time:

  1. The ops hub does not exist. Standing it up is an operator action — GCP project, WorkOS org with the operator role, seed script against real credentials. Every dependent path is built and unit-tested against its port, but nothing has talked to a real one.
  2. Docker is unavailable here, so the plan's own flagged risk — "prove the Docker image still builds with a workspace devDep in the closure" — cannot be discharged. Landing the Vite/Dockerfile changes unverified would ship exactly that risk.

0433 is marked [-] at 34/52 implementation, 12/20 validation.

Verification

  • pnpm test12,249 passed, 1 skipped
  • pnpm typecheck — clean
  • pnpm lint — clean for changed files (one pre-existing warning in sync-gate.test.ts, untouched)
  • pnpm check:gate-controls — passes, and demonstrably fails when the gate is broken
  • site builds; both ADRs render; validate:compare, validate:dist, validate:metrics pass
  • check:exploration-links / -fallow / visual-explorations — pass

siteMetrics.tests bumped 9600 → 12000: the repo's real count had drifted and these tests pushed it past the 25% staleness gate.

🤖 Generated with Claude Code

xNet Test added 15 commits August 1, 2026 22:16
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Signed-off-by: xNet Test <test@xnet.dev>
Reuse React + Tailwind + @xnetjs/ui (zero @xnetjs/* deps) instead of
hand-rolled HTML, and keep the operational record on xNet itself so the
signed change log becomes the audit trail. Split: xNet for the record,
REST for the readings — metrics stay off the change log (0323).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Signed-off-by: xNet Test <test@xnet.dev>
… ADR-31/32

Sixteen decisions settled by interview; 0431 stays as the research with a
forward pointer. Adds two findings 0431 did not have: the tiers selling a
99.9% SLO are provisioned scale-to-zero (minInstances keys off the
isolation tier, not the SLA), and four user-facing surfaces claim we
cannot read data the hub indexes for FTS. Both ship before the console.

ADR-31 records the ops-hub dependency, ADR-32 the support consent model,
each with a tripwire. Site build verified locally since CI does not build
site/ on PRs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Signed-off-by: xNet Test <test@xnet.dev>
…lation tier

The warm-instance rule keyed off isolation === 'dedicated-warm', which is
the team plan — best-effort, so it can never burn an error budget. The
three plans that DO publish an objective (community, company at 99.9%,
enterprise at 99.95%) all provisioned scale-to-zero, and region-pinned
was not handled at all. A cold start can spend a large fraction of a
43-minute monthly budget.

availabilityObjective/requiresWarmInstance live in @xnetjs/entitlements
beside PLAN_CATALOG so the provisioner and the control plane's SLO math
read one mapping; slo.ts now delegates to it instead of a second switch.

Exploration 0433 D1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Signed-off-by: xNet Test <test@xnet.dev>
Modelling the cost delta caught a regression in the D1 fix: an
objective-only rule would have dropped team (dedicated-warm but
best-effort) to scale-to-zero. PLAN_PRICING models team with warm: true,
so the price already covers that COGS and removing it would be a silent
downgrade of a paying tier.

Warmth is now a floor from both reasons. Cost delta recorded in 0433:
~$6/mo per SLO tenant, already assumed by the price list.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Signed-off-by: xNet Test <test@xnet.dev>
Five user-facing surfaces claimed xNet Cloud holds only encrypted bytes,
or that the hub never sees plaintext. search-indexer.ts extracts plaintext
from rich text to build the FTS index, and 0343 records that the trusted
tier gives integrity but not confidentiality.

The repo already holds this standard elsewhere — HonestyBox.astro and
HonestMachine.astro explicitly refuse to overclaim E2EE — the cloud
surface had drifted from it. Two of the five (an 'end-to-end encrypted
workspace' claim and a second 'never sees plaintext') were found during
implementation, not in the audit.

privacy.astro is unchanged: 'we cannot read your data with it' scopes to
the billing identity and is accurate.

Exploration 0433 D2.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Signed-off-by: xNet Test <test@xnet.dev>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Signed-off-by: xNet Test <test@xnet.dev>
The in-memory ring held 2000 samples at a 60s probe interval — 33 hours
of a window labelled 30 days — and died with the process, so every deploy
handed the error budget back at 100%. rollout/engine.ts gates fleet
upgrades on that budget, so a restart silently unfroze deploys.

Buckets are one document per tenant per hour over the existing DocStore
port: ~43 KB per tenant per 30 days, rolled to daily at 30 days and
pruned at 13 months. windowState() distinguishes stale (measurement
broken -> freeze) from young (new tenant -> excluded), so the gate can go
red for the right reason without freezing on every signup.

The probe now waits 30s instead of 5s and records a slow success as a
cold start rather than an outage — sli.ts always documented that intent.
The 30s is a placeholder: no cold-start figure exists in the repo (0433
open question 1), and the constant says so.

Exploration 0433 Phase 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Signed-off-by: xNet Test <test@xnet.dev>
publicStatus hardcoded control-plane: operational, which said only that
the process answered THIS request, and reported an unmeasured hub fleet
as operational. Both are the same defect as an empty sample window
reading 100% available.

Adds an unmeasured ComponentStatus ranked above operational but below
degraded — it can displace a green banner without paging anyone over a
missing probe. The control plane now reports from leased-job staleness;
unwired job reporting renders unmeasured, never green.

Also replaces === on the internal shared secret with a constant-time
compare.

Exploration 0433 decisions 10 and 11.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Signed-off-by: xNet Test <test@xnet.dev>
The shared secret named nobody, and /internal/account/recover clears a
tenant's bound DID so the next device claims their hub — a fleet-wide
takeover primitive with no attributable trace.

Mutating internal routes now require an operator identity plus a typed
reason and reject the shared secret outright; the three read routes keep
it, so cloud-company-metrics.mjs is unchanged. audited() writes the
durable entry BEFORE the action runs, so an action that fails is still
attributable and cannot be suppressed by crashing.

Tier 2 (signed node on the ops hub) publishes async and queues when
unreachable; pendingCount() is the alertable gap. Operator identity
splits authorisation (WorkOS role claim) from attribution (DID binding),
and retired bindings stay resolvable so 12-month audit history outlives
the key that signed it.

Exploration 0433 decisions 4, 5 and 11; ADR-31.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Signed-off-by: xNet Test <test@xnet.dev>
…ble gate

Adds the ops-hub publisher (audit entries become nodes signed by the
operator DID, readable back through the hub's existing
/audit/authors/:did/changes), a 12-hour operator session cookie kept
distinct from the 7-day tenant one, and the WorkOS role claim reader.

The gate negative control (0430's rule) plants five violations fleetGate
MUST flag plus three it must NOT, and joins the existing
check:gate-controls aggregate. Verified it goes red: removing the stale
check from fleetGate fails the control, restoring it passes.

Privacy policy now states what support can see, that content needs
per-incident consent, and that the signed access log is kept 12 months
and survives account deletion — so look-then-delete cannot erase its own
evidence.

siteMetrics.tests bumped 9600 -> 12000; the repo's real count (12050) had
drifted and these tests pushed it past the 25% staleness gate.

Exploration 0433 Phase 0; ADR-31, ADR-32.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Signed-off-by: xNet Test <test@xnet.dev>
Solves the bootstrap: the console requires a named operator, but there is
no console through which to name the first one. Writes the binding
directly and refuses when no GCP project is configured rather than
seeding an in-memory store that vanishes on exit. --retire never deletes,
so 12-month audit history keeps a resolvable key.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Signed-off-by: xNet Test <test@xnet.dev>
Decision 15's whole point: if the record vanished with the account, an
operator could read someone's data and erase the evidence by deleting
them. Also pins the entry shape so no field can carry tenant content.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Signed-off-by: xNet Test <test@xnet.dev>
…lan (12/20)

Ship-first defects and all of Phase 0 landed. Phases 1-3 deferred: the
ops hub is an operator action that does not exist yet, and Docker is
unavailable so the plan's own flagged risk (workspace devDep in the image
closure) cannot be discharged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Signed-off-by: xNet Test <test@xnet.dev>
…iality copy

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Signed-off-by: xNet Test <test@xnet.dev>
@crs48
crs48 temporarily deployed to pr-692 August 2, 2026 05:50 — with GitHub Actions Inactive
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@crs48, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 21 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1d3069a2-9b25-4e9a-8fe6-ad463bbe9e11

📥 Commits

Reviewing files that changed from the base of the PR and between 2dc530a and a2f1017.

📒 Files selected for processing (38)
  • apps/cloud/src/dashboard.ts
  • apps/cloud/src/observability/buckets.test.ts
  • apps/cloud/src/observability/buckets.ts
  • apps/cloud/src/observability/gate-control.test.ts
  • apps/cloud/src/observability/health.ts
  • apps/cloud/src/observability/sli.ts
  • apps/cloud/src/observability/slo.ts
  • apps/cloud/src/observability/status.test.ts
  • apps/cloud/src/observability/status.ts
  • apps/cloud/src/ops/audit.test.ts
  • apps/cloud/src/ops/audit.ts
  • apps/cloud/src/ops/operator.test.ts
  • apps/cloud/src/ops/operator.ts
  • apps/cloud/src/ops/publisher.test.ts
  • apps/cloud/src/ops/publisher.ts
  • apps/cloud/src/ops/session.ts
  • apps/cloud/src/secret-compare.test.ts
  • apps/cloud/src/secret-compare.ts
  • apps/cloud/src/server.test.ts
  • apps/cloud/src/server.ts
  • docs/explorations/0431_[_]_XNET_CLOUD_OPERATOR_CONSOLE_SRE_AND_SUPPORT.md
  • docs/explorations/0433_[-]_OPERATOR_CONSOLE_THE_DECIDED_PLAN.md
  • docs/explorations/STALE.md
  • package.json
  • packages/cloud/src/provisioner/adapters/cloud-run-litestream.test.ts
  • packages/cloud/src/provisioner/adapters/cloud-run-litestream.ts
  • packages/entitlements/src/index.ts
  • packages/entitlements/src/plans.test.ts
  • packages/entitlements/src/plans.ts
  • scripts/check-sli-gate.mjs
  • scripts/cloud-seed-operator.mjs
  • site/public/llms-full.txt
  • site/src/content/docs/docs/architecture/decisions.mdx
  • site/src/data/changelog/2026-08-01-cloud-plans-with-an-uptime-guarantee-no-.json
  • site/src/data/compare.ts
  • site/src/data/siteMetrics.ts
  • site/src/pages/cloud/index.astro
  • site/src/pages/privacy.astro

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

github-actions Bot added a commit that referenced this pull request Aug 2, 2026
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Preview removed for PR #692.

github-actions Bot added a commit that referenced this pull request Aug 2, 2026
xNet Test added 2 commits August 1, 2026 23:00
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Signed-off-by: xNet Test <test@xnet.dev>
Signed-off-by: xNet Test <test@xnet.dev>
@crs48
crs48 temporarily deployed to pr-692 August 2, 2026 06:01 — with GitHub Actions Inactive
@crs48
crs48 merged commit 9900eb3 into main Aug 2, 2026
21 checks passed
@crs48
crs48 deleted the claude/0433-operator-console-the-decided-plan branch August 2, 2026 06:10
github-actions Bot added a commit that referenced this pull request Aug 2, 2026
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