feat(api): /api/v1 lifecycle endpoints — suspend / resume / delete (p1b)#28
Merged
Conversation
The remote API could only READ. Add the first write/lifecycle slice so a key
can actually manage existing hostings:
* POST /api/v1/hostings/:id/suspend cap HostingSuspend (sync)
* POST /api/v1/hostings/:id/resume cap HostingSuspend (sync)
* DELETE /api/v1/hostings/:id cap HostingDelete → 202 {job_id}
Authz is safe-by-construction, not a mint-time fence: a shared resolve_manage()
resolves :id (id OR domain) to its detail + owning node via find_hosting_anywhere,
then reuses the browser UI's own require_hosting_access gate (capability held →
scope_all reaches every hosting → non-scope_all key fails closed until per-key
grants land), swapping its HTML 403 for the JSON envelope. So when per-tenant
keys ship these endpoints are already scoped — no follow-up fence needed.
Delete runs as the same background job as the UI (nginx reload / acme cleanup /
DROP DATABASE / rm -rf / userdel are slow), audited as actor apikey:<label>;
poll GET /api/v1/jobs/:id for completion. Suspend/resume are synchronous and
return the new state. All reuse the existing HostingSuspend/Resume/Delete RPCs.
Still TODO(api-p1b): POST /api/v1/hostings (create — large body + placement) and
tenant read-scoping so non-scope_all keys can be minted.
e2e test: all three endpoints 401 without / with an unknown Bearer key (guards
routing + method wiring). clippy -D warnings clean; hyperion-web suite green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Continues the remote management API (p1b). The API could only read; this adds the first write slice so a key can manage existing hostings.
Endpoints
/api/v1/hostings/:id/suspend{id, state:"suspended"}/api/v1/hostings/:id/resume{id, state:"active"}/api/v1/hostings/:id{job_id}:idaccepts a hosting id or a domain (same disambiguation as the read detail route).Authz — safe by construction
A shared
resolve_manage()resolves:id→ detail + owning node viafind_hosting_anywhere, then reuses the browser UI's ownrequire_hosting_accessgate (capability held →scope_allreaches every hosting → a non-scope_allkey fails closed until per-key grants land), swapping its HTML 403 for the JSON envelope. This is deliberately not a mint-time-only fence — the exact gap the earlier audit flagged — so when per-tenant keys ship in the next slice these endpoints are already scoped.Delete = background job
Delete reuses the same job the UI spawns (nginx reload, acme cleanup, DROP DATABASE, rm -rf, userdel are slow), audited as actor
apikey:<label>. Clients pollGET /api/v1/jobs/:id(already gated toscope_allin #26). Suspend/resume are synchronous and reuse the existingHostingSuspend/HostingResumeRPCs.Not in scope (still
TODO(api-p1b))POST /api/v1/hostings(create) — large request body + node placement, its own slice.scope_allkeys can finally be minted.Test
New e2e test: all three endpoints return 401 with no key and with an unknown Bearer key — guards routing + method wiring (a 404/405 would mean a mistake).
clippy -D warningsclean; fullhyperion-websuite green.🤖 Generated with Claude Code