Documentation
¶
Overview ¶
Package api is the control plane's whole HTTP surface: every route the platform serves, and the dispatcher deciding which credential may reach which route. Five surfaces share one http.ServeMux, four credentials reach them, and dispatchAuth (server.go) picks the credential by path and runs it before the router — splitting the routes across nested muxes would let ServeMux's own path-cleaning and subtree-slash redirects answer an unauthenticated request before auth had run. Surfaces and credentials do not line up one to one, and the exceptions are the part a reader has to carry: they are collected at the end of the map below rather than left implicit in it.
The public /v1 wire — agents, environments, sessions with their events and resources, vaults and credentials, skills, files — is wire-compatible with Anthropic Managed Agents down to paths, JSON shapes, ID prefixes and the pagination and error envelopes, and takes the management x-api-key (auth.go) everywhere the dual-auth set at the end does not say otherwise; anthropic-version / anthropic-beta and ?beta=true are accepted and ignored. The work API under /v1/environments/{id}/work is the second credential: a BYOC worker presents an environment key as Authorization: Bearer, and workScope asserts that key's environment against the path's, so one key drives one queue and no other (envauth.go, workapi.go). The off-wire /api console namespace mirrors the reference console's own private paths rather than inventing a namespace (consoleapi.go, consoleapikeys.go); it is reached by a verified human whose asserted role must clear the route's declared minimum (identitylane.go), or by the management key, which no dispatch can confine to the console: one credential cannot distinguish its callers, so "console-only" means off the wire and built for the console, nothing more (consoleapi.go). That human lane is not confined to /api — it is the management arm's alternate credential everywhere, tried only when no x-api-key is offered, which is why every route declares its role beside its path in server.go. Fourth, the internal gate-config endpoint, and with it the fourth credential: a session's egress gate authenticates with its per-session gtk_ token and fetches the networking policy and resolved credentials it needs (gateauth.go, gateconfig.go) — off /v1, and registered as a divergence in docs/DIVERGENCES.md. Fifth is the event layer (events.go): the SSE tail of a session's log, and the state-machine triggers a posted batch fires — waking an idle session, resuming a suspended turn, clearing a confirmation gate — which is why sending events is a transaction holding the session row rather than a bare append. Both sit in the dual-auth set below.
The exceptions, then, and they run one way: the environment key is not confined to the work API. Every route a BYOC worker needs to set up and drive its session is dual-auth (dualAuth) — reached by a worker's Bearer environment key, or by a management or human caller, whichever the request carries. That is the session events subtree; the bare GET /v1/sessions/{id}; the GET skill reads at and under /v1/skills/{id} — the skill itself, its versions, a version, and a version's /content (isSkillReadPath, server.go); and the GET /v1/files/{id}/content download, which is the worker's own SetupSkills and SetupFiles path. Skill content and file content therefore do NOT need a management key. What keeps the lane narrow is per-resource scoping inside the handlers rather than the dispatcher: a session route's key must own the session (requireEnvironmentKeyForSession), a file download's key must belong to an environment in which some session mounts that file (downloadFile), and skills, workspace-global resources every environment's sandboxes consume, need no scoping at all. Everything else on /v1 — the collections, the file metadata read, every mutation — is management-only, the work API excepted: it runs the other way, taking the environment key and nothing else, since resolveEnvironmentKey demands a Bearer and a management x-api-key never satisfies it (envauth.go). Outside the events subtree the dual-auth routes are GET-only.
The cross-cutting fact no single file makes obvious: dispatchAuth classifies on r.URL.EscapedPath() while ServeMux matches the DECODED path, and the asymmetry cuts both ways on purpose. An encoded %2F cannot forge a segment the router does not also see, so an environment key can never be admitted to a management-only handler. In the other direction a percent-encoded spelling of a machine route (/%77ork) fails the lane predicates, falls through to the management arm, and is then decoded and routed to that machine registration anyway — arriving there, with identity enabled, on the human lane with a real principal. What refuses it is identity.RoleNone on those registrations, which no role satisfies. RoleNone on the work and gate routes is therefore a live denial, not a placeholder (TestAnEncodedPathCannotSlipPastTheWorkLane).
Index ¶
- Constants
- func EnsureAPIKey(ctx context.Context, pool *pgxpool.Pool, name, key string) error
- func IssueEnvironmentKey(ctx context.Context, pool *pgxpool.Pool, environmentID, name string) (string, error)
- func NewHandler(pool *pgxpool.Pool, blobs blob.Store, cipher secrets.Cipher, ...) http.Handler
- func RevokeEnvironmentKey(ctx context.Context, pool *pgxpool.Pool, environmentID, keyID string) (found bool, err error)
- func StartDeploymentScheduler(ctx context.Context, pool *pgxpool.Pool, blobs blob.Store, ...)
- func StartDreamRunner(ctx context.Context, pool *pgxpool.Pool, blobs blob.Store, ...)
- func StartFileRetention(ctx context.Context, pool *pgxpool.Pool, q *ObjectDeleteQueue)
- func StartMemoryRetention(ctx context.Context, pool *pgxpool.Pool)
- func StartPendingObjectDeletes(ctx context.Context, pool *pgxpool.Pool, blobs blob.Store, ...)
- type DreamRunnerConfig
- type EnvironmentKey
- type ImportSummary
- type ManagementKey
- type ObjectDeleteQueue
- type Option
Constants ¶
const ( KeyStatusActive = "active" KeyStatusInactive = "inactive" KeyStatusArchived = "archived" KeyStatusExpired = "expired" )
The three states an operator may set, and the fourth the server computes.
The settable set is the reference's own, quoted from the 400 its server returned to a `{"status":"deleted"}` probe: "status: Input should be 'active', 'inactive' or 'archived'" (#378). `expired` is **rendered, never stored** — storing it would need a sweeper to keep true, and a key that lapsed while the sweeper was down would keep authenticating.
const ( // MetricDeploymentFires counts fire attempts by outcome: "created" (a run // row with a session), "failed" (a run row settled on a classified error, // sub-attributed by error.type) and "abandoned" (an unclassified failure // rolled the whole transaction back, so there is no run row and no error // type to label it with — without this outcome an infrastructure failure // would be invisible in the only place that counts fires). A lost claim is // not a fire — another replica's is — and counts nothing. Exported so the // test can assert the exact name. MetricDeploymentFires = "deployment.fires" // MetricDeploymentOccurrencesSkipped counts occurrences a catch-up // collapse passed over (§3.6) — the one number that makes a dropped // backlog debuggable. It rides the claim, not the tick: only the winner // adds, after its commit, so a rolled-back claim cannot leave a count the // retry would double. MetricDeploymentOccurrencesSkipped = "deployment.occurrences.skipped" // MetricDeploymentTickDuration times one whole sweep. A tick approaching // the tick interval is the signal that the fleet has outgrown one sweep — // and that deploymentFireConcurrency is wrong. MetricDeploymentTickDuration = "deployment.tick.duration" )
const ( // MetricDreamTransitions counts dream status transitions by the status // moved to. A closing pass moves no status and counts nothing. MetricDreamTransitions = "dream.transitions" // MetricDreamStageTurns records the model turns a running dream's current // stage has spent — every thread's counted (§3.3) — so the turn cap has a // distribution behind it rather than only a tripwire. MetricDreamStageTurns = "dream.stage_turns" // MetricDreamTickDuration times one whole sweep, the scheduler's twin. MetricDreamTickDuration = "dream.tick_duration" )
const ( MetricFileUploads = "files.uploads" MetricFileUploadBytes = "files.upload.bytes" MetricFileDownloadBytes = "files.download.bytes" )
Files registry instruments (docs/plan/08_files.md observability table), mirroring the skills registry names. Attribute cardinality is bounded: outcome only — file ids belong in logs and span attributes, never in metric labels. Exported so filesmetric_test.go can assert the exact names and labels.
const ( MetricSkillUploads = "skills.uploads" MetricSkillUploadBytes = "skills.upload.bytes" MetricSkillDownloadBytes = "skills.download.bytes" )
Skill registry instruments (docs/plan/06_skills.md observability table). Attribute cardinality is bounded: outcome only — skill ids belong in logs and span attributes, never in metric labels. Exported so skillsmetric_test.go can assert the exact names and labels.
const EnvironmentKeyTTL = 365 * 24 * time.Hour
EnvironmentKeyTTL is how long an issued environment key authenticates. It matches the reference console's own year — its issuance response reports expires_in: 31536000 — and is not configurable: an operator wanting a shorter-lived credential revokes it, and the alternative, a per-key TTL, is a policy the reference does not offer and nobody has asked for.
const IssuedKeyPrefix = "sk-map-api01-"
IssuedKeyPrefix marks a management key this platform minted, beside plan 30's `sk-map-env01-` for worker credentials. It is public by construction: it is the same for every key, so showing it identifies the *kind* of credential without revealing anything about a particular one.
const ( // MetricExpiredFilesPurged counts rows the sweep removed. Exported so the // test can assert the exact name; no attributes, since the only candidates // would be file ids. MetricExpiredFilesPurged = "files.expired.purged" )
Expired-file retention (#655, plan 49 slice 2). The reference publishes both halves of what happens at expires_at: the content stops being retrievable immediately, while "Its metadata (GET /v1/files/{file_id}) remains readable for up to 30 days, with expires_at in the past". Slice 1 built the first half out of a predicate every reader composes; this is the second, and it is the only thing in this platform that removes a file nobody asked to remove.
That reverses nothing. discardUncommittedObject is about objects whose row never committed — accidents nobody can enumerate, where a sweep would have to guess what is live. An expired file is the opposite: the row names the object, and the deletion is the lifecycle the client bought at upload. That helper's argument stays true where it stands, and is now the only thing left on this side of the line (#703).
It lives beside the file routes for memoryretention.go's reason — it is written from the same facts they are — and the controlplane hosts it because that binary already holds the pool and the blob store, and a deployment whose environments are all self_hosted runs no executor.
Two things migration 0037's comment says are no longer true, and a merged migration is immutable — comments included — so the corrections live here, the same move memoryretention.go makes for 0029's. 0038's own two follow them.
It calls this plan 48, the number the plan had while slice 1 was in review; an earlier-opened PR held that number and merged first, so the plan is 49.
And it argues against an index on the ground that "a partial index over `expires_at IS NOT NULL` would cost every upload a write", which is backwards: a partial index does not index the rows its predicate excludes, so an upload with no lifetime pays nothing at all and only an expiring one pays. Migration 0038 adds that index, because the scan 0037 was willing to accept now runs once an hour on every deployment, forever.
0038 is immutable in its turn, and two things it says need the same treatment (#698). It describes the table as growing "a row per upload and a row per harvested deliverable, pruned by nothing except this sweep", and both halves are wrong about deliverables: the executor's harvest inserts them without an expires_at (internal/executor/harvest.go names id, filename, mime_type, size_bytes, downloadable, scope_type and scope_id), so this sweep never sees one — what removes them is a session delete, which takes every row scoped to the session, and the next replacing harvest, which drops the whole snapshot before writing the new one. The index still earns its place on the uploads, which do expire.
And it omits the note 0031 and 0035 both carry: migrate.go applies every pending file inside one transaction, so CREATE INDEX cannot be CONCURRENTLY. This form takes SHARE on files for the build — 0035's own wording for the same situation — so reads go on and writes wait, and they wait not for the build but for the whole migration transaction, since migrate.go commits once. On the very table 0038 argues grows without bound, that is an upgrade stall on uploads, harvests and deletes that an operator gets no warning of.
const ( // MetricMemoryVersionsPruned counts rows the sweep removed. Exported so // the test can assert the exact name; no attributes, because the only // candidates for one would be store or memory ids. MetricMemoryVersionsPruned = "memory.versions.pruned" )
Memory-version retention (#476, plan 36 scope decision 5). The reference publishes the window and withholds the count: "Versions are retained for 30 days; however, the recent versions are always kept regardless of age, so memories that change infrequently might retain history beyond 30 days." How many "recent versions" survive is stated nowhere, and no recording against the platform can reveal a server-side background policy, so the count is ours — five, registered in docs/DIVERGENCES.md.
It lives beside the memory routes rather than in a package of its own because it is the same three facts they are written from: the head pointer is memories.memory_version_id, a version's lineage is (memory_store_id, memory_id), and neither is a foreign key. The controlplane hosts it because that binary already holds the pool and serves every memory route — a deployment whose environments are all self_hosted runs no executor at all.
The sweep leans on migration 0029's memory_versions_memory_idx (memory_store_id, memory_id, created_at DESC, id DESC), which makes the per-candidate "newest keep" lookup an index-only scan of exactly keep rows. That index's own comment predates this file and calls the history one "nothing ever prunes"; a merged migration is immutable, so the fact lives here instead. Narrowing or dropping it would turn a seconds-long sweep into one that walks a whole store's history per candidate row.
const MetricSessionResources = "session.resources"
MetricSessionResources counts session resource mutations (create-attach, add, delete, token rotation) by outcome. Outcome-only labels: session/resource/file ids ride the structured logs, never the metric (plan decision 9). Exported so the integration test can assert the name and labels.
Variables ¶
This section is empty.
Functions ¶
func EnsureAPIKey ¶
EnsureAPIKey makes key the one live credential for the named logical key: it inserts (or reactivates) the hash and archives every other live key under the same name. That gives rotation-by-restart semantics — changing CONTROLPLANE_API_KEY and restarting cmd/controlplane retires the previous key instead of leaving it valid forever. All replicas must therefore share one key value per name; replicas booting with *different* values for one name race, and api_keys_one_live_unissued resolves that by failing the loser's transaction rather than leaving the name with two live credentials.
It only ever writes rows with created_by NULL, which is what puts them under that index and marks them env-var-managed. A key issued over the console records its issuer and is deliberately outside the one-live rule (plan 32).
func IssueEnvironmentKey ¶ added in v0.3.0
func IssueEnvironmentKey(ctx context.Context, pool *pgxpool.Pool, environmentID, name string) (string, error)
IssueEnvironmentKey mints a worker credential for an environment and returns it in plaintext — the only moment it exists anywhere but the caller's memory, since the database receives nothing but its hash. name labels the key so an operator can tell one host's credential from another's when revoking one.
An environment may hold any number of live keys: issuing does not disturb the ones already out on other hosts. That is the reference console's model, and the reason migration 0021 retired the one-live-key invariant.
Issuance itself is a deliberate divergence: the reference mints environment keys on its console's private backend, with no public wire endpoint, so a self-hostable platform owns the primitive. The consuming side — resolving a Bearer token to its environment — stays wire-locked by the real `ant beta:worker`.
func NewHandler ¶
func NewHandler(pool *pgxpool.Pool, blobs blob.Store, cipher secrets.Cipher, verifier *identity.Verifier, opts ...Option) http.Handler
NewHandler assembles the control-plane HTTP surface over the given pool. blobs is the object store backing skill archives; nil deploys without object storage — everything serves except the storage-backed skill routes, which answer with a configuration error. cipher seals vault credential secrets; nil deploys without one — vault metadata CRUD serves, while the secret-bearing paths (credential create/update with secret fields, the validate probe) answer with a configuration error (fails closed, plan 12 D1). verifier authenticates humans; nil is IDENTITY_MODE=disabled, and the surface is then what it was before plan 31 — no lane, no role check — on every request shape but one: requireAPIKey refuses a repeated x-api-key field in every mode, deliberately (see dispatchManagementAuth).
func RevokeEnvironmentKey ¶ added in v0.3.0
func RevokeEnvironmentKey(ctx context.Context, pool *pgxpool.Pool, environmentID, keyID string) (found bool, err error)
RevokeEnvironmentKey retires one key and leaves the environment's others authenticating. found reports whether the environment held such a key at all, which is how the caller tells a real revocation from a 404 — a key id that belongs to another environment is simply not found here, so revocation cannot reach across environments or confirm that an id exists elsewhere.
Revoking an already-revoked key succeeds and still reports found, so a retry is not an error; coalesce keeps the original revocation timestamp rather than sliding it forward on every repeat.
func StartDeploymentScheduler ¶ added in v0.4.0
func StartDeploymentScheduler(ctx context.Context, pool *pgxpool.Pool, blobs blob.Store, cipher secrets.Cipher)
StartDeploymentScheduler ticks until ctx ends. Safe on every replica at once: the occurrence claim is a unique-index insert, so N replicas cost N-1 briefly-blocked losers per due occurrence and never a duplicate run. blobs and cipher are the handler's own — a fire is a full session creation and needs whatever POST /v1/sessions needs (the cipher is still never dialed at fire time: repository tokens were sealed at deployment create/update, and the fire copies ciphertext).
func StartDreamRunner ¶ added in v0.4.0
func StartDreamRunner(ctx context.Context, pool *pgxpool.Pool, blobs blob.Store, cipher secrets.Cipher, q *ObjectDeleteQueue, cfg DreamRunnerConfig)
StartDreamRunner ticks until ctx ends. Safe on every replica at once: each arm re-reads its dream under FOR UPDATE SKIP LOCKED, so a second replica skips the row rather than duplicating the arm. blobs and cipher are the handler's own — the start arm puts the rendered transcripts and creates a session, which needs whatever POST /v1/sessions needs.
func StartFileRetention ¶ added in v0.4.0
func StartFileRetention(ctx context.Context, pool *pgxpool.Pool, q *ObjectDeleteQueue)
StartFileRetention sweeps until ctx ends. Safe on every replica at once: the DELETE is itself the claim, so a row is returned to exactly one sweeper and only that sweeper owes its objects.
It removes no bytes. It removes rows and writes, in the same transaction, one pending_object_deletes row per object they orphan — so a deployment has one remover of orphaned bytes, the drain, whether the keys came from a session delete or from here (plan 50). A deployment configured without object storage stops being a special case: the debt is recorded the same way, and the half that needs a store is the drain, which such a deployment does not run.
func StartMemoryRetention ¶ added in v0.4.0
StartMemoryRetention sweeps until ctx ends. The statement is idempotent, so two controlplane replicas running it never reach a wrong answer — they cost a duplicate query and, since the reorder below made a boot pass the moment they all take one, whatever lock waiting that duplication implies. The statement removes what it finds in one unbounded DELETE, which is what the first pass over a long-unpruned table costs and what makes that timing worth naming: a control plane restarting faster than that pass commits repeats it from the start at every boot and still prunes nothing, so the order below buys forward progress only where a restart outlasts one DELETE. A bounded batch — fileretention.go's shape — is what would close that, the day a first pass is measured in minutes. It takes row locks only on the rows it deletes, which no plain reader waits on — a redaction's `SELECT … FOR UPDATE` on one of those rows would, for as long as the delete runs.
func StartPendingObjectDeletes ¶ added in v0.4.0
func StartPendingObjectDeletes(ctx context.Context, pool *pgxpool.Pool, blobs blob.Store, q *ObjectDeleteQueue)
StartPendingObjectDeletes drains the queue until ctx ends: once per interval, and again whenever a delete in this process wakes it. It never returns an error — a store that is refusing today is a row that is still owed tomorrow, which is the whole point of the table.
Types ¶
type DreamRunnerConfig ¶ added in v0.4.0
type DreamRunnerConfig struct {
// TickInterval paces the sweep, and is therefore a dream's start latency
// in the steady state. A dream pending when the process starts is normally
// taken by the pass the loop makes before its first wait (#699) — normally,
// because that pass skips when the shared sweep budget is saturated or the
// clock read fails, and then the dream waits out an interval as it did
// before.
TickInterval time.Duration
// Timeout is a dream's runtime budget from creation, in pending as in
// running → error.type "timeout".
Timeout time.Duration
// MaxInputBytes caps the input store's content → "input_memory_store_too_large".
MaxInputBytes int64
}
DreamRunnerConfig is the operator's three knobs (§4.7), read from the controlplane's environment.
type EnvironmentKey ¶ added in v0.3.0
type EnvironmentKey struct {
ID string
Name string
CreatedAt time.Time
// ExpiresAt is nil for a key minted before keys carried expiries; such a
// key stays live until revoked, which is what it was promised on issue.
ExpiresAt *time.Time
}
EnvironmentKey is an issued credential's metadata — everything about a key that outlives its issuance. The secret is not part of it: only the hash is stored, so no surface can render the key a second time.
func ListEnvironmentKeys ¶ added in v0.3.0
func ListEnvironmentKeys(ctx context.Context, pool *pgxpool.Pool, environmentID string, limit, offset int) ([]EnvironmentKey, int, error)
ListEnvironmentKeys returns an environment's un-revoked credentials, newest first, with the total that matched so the caller can page. A revoked key is omitted rather than shown as retired — revocation is final, and the reference console drops the row too. An expired key is still listed: an operator whose worker has stopped connecting needs to see the credential it is failing on.
type ImportSummary ¶ added in v0.2.0
type ImportSummary struct {
Imported []string // "name version" pairs landed this run
Skipped []string // already present at this version
Failed map[string]error // directory → why it did not import
}
ImportSummary reports one operator-import run.
func ImportAnthropicSkills ¶ added in v0.2.0
func ImportAnthropicSkills(ctx context.Context, pool *pgxpool.Pool, blobs blob.Store, dirs []string, version string) (*ImportSummary, error)
ImportAnthropicSkills is the controlplane's run-once operator import (docs/plan/06_skills.md slice 3): each dir is a skill directory from a local checkout of github.com/anthropics/skills, validated exactly like an upload and landed as a source='anthropic' skill whose id is the SKILL.md name (the reference catalog's short-name ids) at the given date-based version. Idempotent per (skill, version): an existing version is skipped without touching storage. A directory that fails to validate is logged and skipped; the returned error reports that some directories failed.
The checkout's content is read at the operator's machine and never enters this repository — the reference document skills are source-available, not open source (the plan's license red lines).
type ManagementKey ¶ added in v0.3.0
type ManagementKey struct {
ID string
Name string
// Status is the rendered status, so it may read `expired`, which is not a
// value any row holds.
Status string
PartialKeyHint string
CreatedAt time.Time
// CreatedBy is nil for a key nobody issued — one seeded from
// CONTROLPLANE_API_KEY. That is the same predicate api_keys_one_live_unissued
// keys on, so "nil issuer" and "env-var-managed" are one fact, not two.
CreatedBy *string
// ExpiresAt is nil for a key that never expires: the console's own "Never".
ExpiresAt *time.Time
}
ManagementKey is an `x-api-key` credential's metadata: everything about a key that outlives its issuance. The secret is not part of it — only the hash is stored, so no surface can render the key a second time, and PartialKeyHint is the only trace of the plaintext that survives.
func IssueManagementKey ¶ added in v0.3.0
func IssueManagementKey(ctx context.Context, pool *pgxpool.Pool, name string, expiresAt *time.Time, createdBy string) (string, ManagementKey, error)
IssueManagementKey mints a management credential and returns it in plaintext — the only moment it exists anywhere but the caller's memory, since the database receives nothing but its hash — alongside the row a caller renders.
createdBy must be non-empty, and every caller is an authenticated route, so it always is: it is the `principal_` id of the human who issued the key over SSO, or the `apikey_` id of the machine credential that did. Beyond audit it is load-bearing, because non-NULL is exactly what puts this row **outside** api_keys_one_live_unissued — issued keys may share a name, as the reference allows, while EnsureAPIKey keeps one-live-per-name over the rows it owns.
expiresAt is the caller's choice and may be nil, which means never. That is a deliberate divergence from environment keys' server-fixed EnvironmentKeyTTL, and it is the reference's: an operator issuing a management credential picks its lifetime; a worker credential's lifetime is the platform's business.
func ListManagementKeys ¶ added in v0.3.0
ListManagementKeys returns every management key, newest first.
Every one, including archived rows and including the key seeded from CONTROLPLANE_API_KEY. Archived rows are returned because the reference returns them — its console filters them client-side — and because an archive that erased the row would erase the evidence that a credential once existed. The bootstrap key is returned because it is a management key: a listing that hid the credential the operator is authenticating with would be lying about what can reach this API. It renders with a null issuer, which is the honest answer to "who created this" for a key an environment variable did.
No paging. The reference's console list is a bare array with no pagination at all, and this table holds one row per issued credential plus the bootstrap one — an operator-paced count, not a growing log.
And no index, which settles a question migration 0024 left open. Its closing comment declined `api_keys (created_at DESC)` as build-ahead and said "the listing that would want one lands in slice 2"; the listing has landed and does not want one. A migration is immutable once merged, so the reconsideration has to be recorded where a reader will meet it rather than by editing that file: the sort is over an operator-paced table with no growth driver — issuance is a deliberate admin action, and nothing else writes rows — so an index would be paid for on every write to speed a read that has nothing to scan. If a deployment ever does accumulate keys, the missing bound is the *response size*, which an index does not fix; that would be paging, and the reference's own surface has none to mirror.
type ObjectDeleteQueue ¶ added in v0.4.0
type ObjectDeleteQueue struct {
// contains filtered or unexported fields
}
ObjectDeleteQueue is the wake shared between the handlers that enqueue and the sweeper that drains, within one process. A capacity-one channel: the request is "look now", so several endings collapse into the one sweep that sees all of them.
func NewObjectDeleteQueue ¶ added in v0.4.0
func NewObjectDeleteQueue() *ObjectDeleteQueue
NewObjectDeleteQueue returns the wake. A nil queue is legal everywhere and means no wake — the sweeper's interval still drains, which is what every test that does not care about latency gets.
func (*ObjectDeleteQueue) Wake ¶ added in v0.4.0
func (q *ObjectDeleteQueue) Wake()
Wake asks this process's sweeper to drain now. Non-blocking and infallible by construction: it is called after a commit, where nothing may fail the request that has already succeeded, and everything it does the interval also does.
type Option ¶ added in v0.4.0
type Option func(*server)
Option is a handler setting with no collaborator behind it — variadic so a deployment that wants none writes none.
func WithDreamRunner ¶ added in v0.4.0
func WithDreamRunner() Option
WithDreamRunner tells the handler that this process ticks a dream runner, which is what lets POST /v1/dreams accept a create (plan 41 §4.7).
func WithObjectDeletes ¶ added in v0.4.0
func WithObjectDeletes(q *ObjectDeleteQueue) Option
WithObjectDeletes hands the handler the wake of the sweeper running beside it (plan 50), so a delete's enqueued keys are drained now rather than at the sweeper's next interval. Without it the deployment is correct and a little slower to free bytes, which is the deliberate shape of a wake that nothing depends on.
Source Files
¶
- agents.go
- apikeys.go
- auth.go
- consoleapi.go
- consoleapikeys.go
- deploymentparse.go
- deploymentresources.go
- deploymentruns.go
- deployments.go
- deploymentscheduler.go
- deploymentwire.go
- doc.go
- dreamprompt.go
- dreamrunner.go
- dreams.go
- dreamstart.go
- envauth.go
- environments.go
- envkeys.go
- errors.go
- events.go
- fileretention.go
- files.go
- filesmetrics.go
- filesupload.go
- gateauth.go
- gateconfig.go
- identitylane.go
- memories.go
- memoryretention.go
- memorystores.go
- memoryversions.go
- objectdeletes.go
- page.go
- principals.go
- roster.go
- runnerguard.go
- server.go
- sessionresources.go
- sessions.go
- skills.go
- skillsimport.go
- skillsmetrics.go
- skillsupload.go
- threads.go
- threadstate.go
- vaultcredauth.go
- vaultcredentials.go
- vaults.go
- vaultvalidate.go
- wire.go
- workapi.go
- worktokenauth.go