rest

package
v0.9.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 27, 2026 License: Apache-2.0 Imports: 56 Imported by: 0

Documentation

Overview

Package rest contains the inbound HTTP adapters.

This file serves only the operations endpoints on the internal port. Business endpoints are generated from api/openapi.yaml (ADR-0004) and do not belong here.

Index

Constants

View Source
const APIBasePath = "/api/v1"

APIBasePath is the one major path of the API (api-guidelines.md §2). The specification's paths are relative to it, so it is prepended once when the generated routes are registered rather than repeated in every path of the contract.

View Source
const IdempotencyKeyHeader = "Idempotency-Key"

IdempotencyKeyHeader is the client's handle on a repeated write (api-guidelines.md §5).

View Source
const ProblemContentType = "application/problem+json"

ProblemContentType is the media type of RFC 9457. Not application/json - a client distinguishes an error from a payload by the media type.

View Source
const ReplayedHeader = "Idempotent-Replayed"

ReplayedHeader tells a client that it is looking at a stored answer rather than a fresh one. Not in the contract as a promise, and deliberately so: a client must behave identically either way. It exists for the person debugging why a request "did nothing".

View Source
const RequestIDHeader = "X-Request-Id"

RequestIDHeader is the header a client may send and always gets back. A load balancer or a browser extension that already assigns one keeps its value, so one identifier spans the whole hop chain (api-guidelines.md §6).

View Source
const ShownOnceHeader = "X-Hubtask-Shown-Once"

ShownOnceHeader is how a handler marks an answer that carries a credential shown once - a start password, a token: the replay record keeps its status and no body. Internal: the middleware takes it off the answer before it leaves.

View Source
const TenantHeader = "X-Hubtask-Tenant"

TenantHeader is the third source of tenant resolution (multi-tenancy.md §3). It never wins over the token: a contradiction between the two is refused, not resolved.

View Source
const UIRoute = "GET /*"

UIRoute is the route template every user interface request is recorded under.

One label for all of them, on purpose. A single-page application owns its own routes, so the paths a browser asks for are invented by the client and unbounded - using them would make the metric's cardinality a property of somebody else's router (observability-reliability.md §3.2). What is worth knowing here is how many requests the UI serves and how fast, not which of its screens a person opened.

Variables

BasicRoutes are the mounted trees that take HTTP Basic beside the bearer. The route label is the mount's path (Mounted.Handler).

View Source
var DeferrableRoutes = map[string]bool{

	http.MethodPost + " " + APIBasePath + "/items:bulk": true,

	http.MethodPost + " " + APIBasePath + "/items:query": true,
	http.MethodPost + " " + APIBasePath + "/search":      true,

	http.MethodPost + " " + APIBasePath + "/views/{viewId}:export":           true,
	http.MethodPost + " " + APIBasePath + "/audit:export":                    true,
	http.MethodPost + " " + APIBasePath + "/admin/tenants/{tenantId}:export": true,
}

DeferrableRoutes are the operations that may be refused while the process is busy: the bulk, export and search shapes observability-reliability.md §6 names by class, plus the item query, which is the heaviest read the contract has.

Everything absent from this list is interactive and is never shed. That direction is the safe one, and it is the reason the list is written out rather than derived: a person who cannot tick off a task retries by hand, which adds load instead of removing it, so a route wrongly listed here costs more than a route wrongly missing from it. What keeps it honest is the contract test - every entry has to be a route the router serves, or a typo would be a line that silently never matches.

View Source
var LongLivedRoutes = map[string]bool{
	http.MethodGet + " " + APIBasePath + "/stream": true,
}

LongLivedRoutes are the routes whose requests are parked rather than working: they are admitted without being counted, and they are never shed.

The change stream is the whole of the list and the reason for it. An SSE connection is in flight for as long as somebody has a tab open, so counting it would make the gauge measure subscriptions instead of load - three hundred idle streams would put an installation permanently above any sensible threshold and refuse every export in it, while the process sat idle. The stream has its own admission control for its own resource, and it answers the same 503 with the same Retry-After when it is full (StreamRegistry, T-19).

View Source
var OwnCredentialRoutes = map[string]bool{
	http.MethodPost + " " + APIBasePath + "/mail/inbound": true,

	http.MethodPost + " " + APIBasePath + "/oauth/token": true,

	http.MethodPost + " " + APIBasePath + "/voice/alexa": true,
}

OwnCredentialRoutes carry a credential in the Authorization header that is not an account's - the receiving domain's inbound key (ADR-0092), an OAuth client's Basic credentials - or none at all and prove the caller another way (ADR-0093). The middleware leaves the header to the route, which compares it itself - verifying it as an account token would refuse every delivery.

View Source
var PublicRoutes = map[string]bool{
	http.MethodGet + " " + APIBasePath + "/meta/capabilities": true,

	http.MethodPost + " " + APIBasePath + "/auth/sessions":           true,
	http.MethodPost + " " + APIBasePath + "/auth/sessions:refresh":   true,
	http.MethodPost + " " + APIBasePath + "/auth/invitations:redeem": true,

	http.MethodPost + " " + APIBasePath + "/auth/invitations:inspect": true,

	http.MethodPost + " " + APIBasePath + "/auth/installation:set-up": true,

	http.MethodGet + " " + APIBasePath + "/auth/sign-in-rules": true,

	http.MethodPost + " " + APIBasePath + "/auth/sessions:set-password": true,

	http.MethodPost + " " + APIBasePath + "/auth/password:forgot": true,
	http.MethodPost + " " + APIBasePath + "/auth/password:reset":  true,

	http.MethodPost + " " + APIBasePath + "/auth/address:confirm": true,

	http.MethodPost + " " + APIBasePath + "/auth/password:check": true,

	http.MethodPost + " " + APIBasePath + "/auth/sessions:verify": true,
	http.MethodPost + " " + APIBasePath + "/auth/sessions:link":   true,

	http.MethodPost + " " + APIBasePath + "/auth/sessions:passkey-options": true,
	http.MethodPost + " " + APIBasePath + "/auth/sessions:passkey":         true,

	http.MethodPost + " " + APIBasePath + "/auth/sessions:agree-terms": true,
	http.MethodPost + " " + APIBasePath + "/auth/mfa/totp:enroll":      true,
	http.MethodPost + " " + APIBasePath + "/auth/mfa/totp:confirm":     true,

	http.MethodPost + " " + APIBasePath + "/auth/mfa/passkey:enroll-options": true,
	http.MethodPost + " " + APIBasePath + "/auth/mfa/passkey:enroll":         true,

	http.MethodPost + " " + APIBasePath + "/auth/oidc:start":    true,
	http.MethodPost + " " + APIBasePath + "/auth/oidc:callback": true,

	http.MethodPost + " " + APIBasePath + "/auth/oidc:locate": true,

	http.MethodPost + " " + APIBasePath + "/oauth/token": true,

	http.MethodPut + " " + APIBasePath + "/media/{mediaId}:content": true,
	http.MethodGet + " " + APIBasePath + "/media/{mediaId}:content": true,

	http.MethodGet + " " + APIBasePath + "/calendar/{token}.ics": true,

	http.MethodPost + " " + APIBasePath + "/automation/inbound/{token}": true,

	http.MethodPost + " " + APIBasePath + "/jumble/inbound/{token}": true,

	http.MethodPost + " " + APIBasePath + "/jumble/mail/{token}": true,

	http.MethodPost + " " + APIBasePath + "/mail/inbound": true,

	http.MethodPost + " " + APIBasePath + "/voice/alexa": true,

	calendar.WellKnown: true,
}

PublicRoutes are the operations api/openapi.yaml declares with `security: []`. Everything else needs a credential.

The list is here rather than derived at run time because the generated code does not carry the security requirement - and a list nobody checks would rot, so the contract test compares it against the specification and fails when the two disagree. Fail closed either way: a route missing from this list is authenticated, never the reverse.

View Source
var ShownOnceByHandler = map[string]string{
	http.MethodPost + " " + APIBasePath + "/accounts:invite":                       "the link only where no mail is sent",
	http.MethodPost + " " + APIBasePath + "/accounts/{accountId}:renew-invitation": "the link only where no mail is sent",
	http.MethodPost + " " + APIBasePath + "/accounts/me/passkeys:complete":         "the recovery codes only for the account's first factor",
}

ShownOnceByHandler are the operations whose answer carries a credential only sometimes; their handler marks the answer that does (ShownOnceHeader). Named here so the contract test can tell them from a route somebody forgot.

View Source
var ShownOnceKept = map[string]string{
	http.MethodPost + " " + APIBasePath + "/admin/tenants": "a repeat answers the same workspace (UC-INS-06 check 3)",
}

ShownOnceKept are the operations whose answer carries a credential and whose replay record still keeps it, each with the reason; the contract test accepts them by name. Provisioning is one: a platform repeats the call because it lost the answer, and the workspace's identifier in the replay is how it learns which workspace it made (UC-INS-06 check 3). The owner's redemption token in the copy works once, and the copy goes with the record (the IDEMPOTENCY_KEY sweep).

View Source
var ShownOnceRoutes = map[string]bool{

	http.MethodPost + " " + APIBasePath + "/auth/sessions":              true,
	http.MethodPost + " " + APIBasePath + "/auth/sessions:refresh":      true,
	http.MethodPost + " " + APIBasePath + "/auth/sessions:verify":       true,
	http.MethodPost + " " + APIBasePath + "/auth/sessions:passkey":      true,
	http.MethodPost + " " + APIBasePath + "/auth/sessions:link":         true,
	http.MethodPost + " " + APIBasePath + "/auth/sessions:agree-terms":  true,
	http.MethodPost + " " + APIBasePath + "/auth/sessions:set-password": true,
	http.MethodPost + " " + APIBasePath + "/auth/invitations:redeem":    true,
	http.MethodPost + " " + APIBasePath + "/auth/installation:set-up":   true,
	http.MethodPost + " " + APIBasePath + "/auth/password:reset":        true,
	http.MethodPost + " " + APIBasePath + "/auth/mfa/totp:confirm":      true,
	http.MethodPost + " " + APIBasePath + "/auth/mfa/passkey:enroll":    true,
	http.MethodPost + " " + APIBasePath + "/auth/oidc:callback":         true,
	http.MethodPost + " " + APIBasePath + "/oauth/token":                true,
	http.MethodPost + " " + APIBasePath + "/auth/step-up":               true,
	http.MethodPost + " " + APIBasePath + "/oauth/authorize":            true,

	http.MethodPost + " " + APIBasePath + "/auth/tokens":                                    true,
	http.MethodPost + " " + APIBasePath + "/automation/rules/{ruleId}:rotate-inbound-token": true,
	http.MethodPost + " " + APIBasePath + "/jumble/intake:rotate-token":                     true,
	http.MethodPost + " " + APIBasePath + "/integrations/calendar-feeds":                    true,

	http.MethodPost + " " + APIBasePath + "/integrations/webhooks":                              true,
	http.MethodPost + " " + APIBasePath + "/integrations/webhooks/{webhookId}:rotate-secret":    true,
	http.MethodPost + " " + APIBasePath + "/admin/subscriptions":                                true,
	http.MethodPost + " " + APIBasePath + "/admin/subscriptions/{subscriptionId}:rotate-secret": true,
	http.MethodPost + " " + APIBasePath + "/oauth/clients":                                      true,
	http.MethodPost + " " + APIBasePath + "/auth/mfa/totp:enroll":                               true,
	http.MethodPost + " " + APIBasePath + "/auth/mfa/totp:replace":                              true,
	http.MethodPost + " " + APIBasePath + "/auth/mfa/totp/replacement:confirm":                  true,
	http.MethodPost + " " + APIBasePath + "/auth/mfa/recovery:regenerate":                       true,

	http.MethodPost + " " + APIBasePath + "/accounts:create-managed":                   true,
	http.MethodPost + " " + APIBasePath + "/accounts/{accountId}:issue-start-password": true,
}

ShownOnceRoutes are the operations whose every successful answer carries a credential shown once - a token, a secret, recovery codes, a start password, a session's tokens. The idempotency record of such an answer keeps its status and no body, as ShownOnceHeader marks it for a single answer: a replay record is a copy of the credential in clear, and the database and its backups would carry it (T-18; security.md §8).

A table rather than a header in each handler, because the class belongs to the operation and a table can be held to the contract: the contract test derives the credential-bearing answers from api/openapi.yaml and fails for one missing here. An answer that carries a credential only sometimes - an invitation shows its link only where no mail goes - stays the handler's to mark.

Functions

func AuthBucket

func AuthBucket(perMinute, burst int) func(*http.Request) Bucket

AuthBucket counts per client address, and applies only to the routes where a credential is guessed rather than presented: sign-in, refresh, and the invitation redeemed (T-02, HUBTASK_RATE_LIMIT_AUTH_PER_MINUTE). It stands in front of the attempt ledger, not instead of it - the ledger slows a patient guesser across days, this sheds a fast one before the Argon2id work is even reached.

func ClientAddress

func ClientAddress(r *http.Request) string

ClientAddress is the peer of the connection, never X-Forwarded-For.

That header is written by the client and can only be believed when a proxy that overwrites it sits in front - and this process cannot know whether one does. Trusting it would turn the anonymous limit into decoration, because every request could claim a fresh address. The cost is that everything behind one ingress shares a bucket, which is the safe direction to be wrong in; a trusted-proxy setting belongs with the deployment work that knows the topology.

func CredentialBucket

func CredentialBucket(anonymousPerMinute, tokenPerMinute, burst int) func(*http.Request) Bucket

CredentialBucket counts per presented credential, and per client address when there is none. The two carry different budgets: an anonymous caller is a stranger, a token is somebody the installation knows (multi-tenancy.md §4).

The credential is fingerprinted rather than used as the key. The map ends up in a heap dump, and a heap dump with live tokens in it is a second incident on top of the first (rule 10).

func FeedBucket

func FeedBucket(perMinute, burst int) func(*http.Request) Bucket

FeedBucket counts per calendar feed token, and applies to nothing else.

The public .ics route is the one place where a credential arrives in the URL, so the bucket in front of it has to be keyed on that credential rather than on the caller's address: a household behind one address may hold several subscriptions, and one client polling hard must not shed another person's calendar. It sits beside the credential bucket rather than instead of it - a caller trying tokens is still an anonymous stranger to that one, which is what bounds the guessing, while this bounds the polling (security.md §4 T-21).

The token is fingerprinted for the reason a bearer credential is: the map ends up in a heap dump, and a heap dump with live feed URLs in it is a second incident on top of the first.

func OverrideBucket

func OverrideBucket(burst int) func(*http.Request) Bucket

OverrideBucket is the per-workspace request-rate ceiling (multi-tenancy.md §4), a level of its own after authentication: it engages where an operator's exception, the workspace's plan or the installation's default sets one, keyed per credential like the credential level - a workspace's ceiling is "per token", not a shared pool. Where none is set it answers the empty bucket and only the credential level applies.

func TenantBucket

func TenantBucket(perMinute, burst int) func(*http.Request) Bucket

TenantBucket counts per tenant, so that one busy token cannot spend a whole tenant's budget and one busy tenant cannot spend the installation's. It applies only once authentication has run.

func WriteMethodNotAllowed

func WriteMethodNotAllowed(w http.ResponseWriter, requestID string)

WriteMethodNotAllowed answers a request that reached a route but not one of its methods. The caller sets the Allow header first - without it a 405 tells a client nothing it can act on.

func WriteProblem

func WriteProblem(w http.ResponseWriter, err error, requestID string)

WriteProblem sends the error as RFC 9457. It never fails visibly: at that point the status is already written, and a broken response body is a log entry, not a second error.

func WriteSecurityHeaders

func WriteSecurityHeaders(header http.Header, contentSecurityPolicy string)

WriteSecurityHeaders writes the response header set of security.md §9, with the caller's content security policy.

The policy is a parameter because there are two origins in this process and they need different ones: the API answers JSON and gets `default-src 'none'`, the embedded UI answers a document and would be unable to load its own script under that (ADR-0028). Everything else is identical and stays defined once, here, so that adding a header adds it to both.

func WriteTooLarge

func WriteTooLarge(w http.ResponseWriter, limit int64, requestID string)

WriteTooLarge refuses a body over the configured limit. The limit is named in the parameters, because a client that does not know it can only guess at a smaller request.

func WriteUnauthenticated

func WriteUnauthenticated(w http.ResponseWriter, err error, requestID string)

WriteUnauthenticated answers a refused credential. A 401 without WWW-Authenticate is incomplete (RFC 9110 §11.6.1): the client is told nothing about how to authenticate.

A 403 travels through here too - a valid token belonging to a disabled account is not a reason to ask for the credential again - which is why the header is set from the mapped status rather than assumed.

Types

type AddressedMailDeliverer

type AddressedMailDeliverer interface {
	Authorized(header string) bool
	Deliver(ctx context.Context, recipient string, raw []byte) (jumbleservice.MailDeliveryResult, error)
}

AddressedMailDeliverer serves the receiving domain's inbound route. Not a catalogue entry: it answers the installation's inbound key, which no person holds.

type AlexaAnswerer

type AlexaAnswerer interface {
	Handle(ctx context.Context, header http.Header, body []byte) (voice.Response, error)
}

AlexaAnswerer serves Alexa's endpoint. Not a catalogue entry: it proves its caller with Amazon's signature and the linked account's token, not with this API's credentials.

type Authenticated

type Authenticated struct {
	Next http.Handler
	// Routes resolves the route template, which is what decides whether a credential is required.
	Routes Router
	// Authenticator is the use case. Nil is not a valid configuration; the composition root wires
	// it (cmd/server).
	Authenticator TokenAuthenticator
	// Locale carries the installation defaults, the last link of the resolution chain.
	Locale env.LocaleConfig
	// BaseHost is the installation's own host name, for reading a tenant subdomain off the
	// request (multi-tenancy.md §3). Empty means no subdomain is ever read - single mode, or an
	// installation that did not state its base URL.
	BaseHost string
}

Authenticated turns a presented credential into the actor of the request.

It authenticates and stops there. Whether the actor may perform the operation is decided by the use case behind the route, in the application layer and nowhere else (ADR-0005, rule 2) - this middleware never reads a scope and never denies an operation.

func (Authenticated) ServeHTTP

func (a Authenticated) ServeHTTP(w http.ResponseWriter, r *http.Request)

type Bounded

type Bounded struct {
	Next http.Handler
	// MaxBodyBytes is the global limit for everything the contract carries as JSON (security.md §9).
	MaxBodyBytes int64
	// MaxUploadBytes is the limit for the one route the contract declares as a byte stream: the
	// content route of a local-storage installation, which stands in for the bucket a presigned
	// upload would have gone to. Bounding it by MaxBodyBytes would make an attachment on
	// such an installation impossible; bounding everything else by this one would hand every JSON
	// endpoint a sixty-four megabyte budget for a body that is never that big.
	MaxUploadBytes int64
	// MaxMailBytes is the limit for the mail intake, whose body is a whole message. A third
	// bound rather than a reuse of either: a mail is far past what a JSON document here ever is,
	// and far below what an upload may be.
	MaxMailBytes int64
	// Timeout is the deadline every handler inherits through the request context.
	Timeout time.Duration
}

Bounded caps what one request may cost: how much body it may send, and how long it may run.

Both are the server's decision, not the client's. A handler that forgets its own deadline still gets one here, which is what rule 7 asks for - no call without a deadline (ADR-0016) - and a body limit is the cheapest defence against T-17 there is.

func (Bounded) ServeHTTP

func (b Bounded) ServeHTTP(w http.ResponseWriter, r *http.Request)

type Bucket

type Bucket struct {
	Key string
	// Limit is the budget per minute, Burst how much of it may be spent at once.
	Limit int
	Burst int
}

Bucket names the budget one request counts against. A zero Key means the level does not apply to this request - an unauthenticated one has no tenant to count against.

type CalendarFeedReader

type CalendarFeedReader interface {
	Execute(ctx context.Context, token integration.FeedToken) (work.ExportedView, error)
}

CalendarFeedReader is the slice of the feed read this controller needs. An interface rather than the service, so the route can be tested without a database and the presentation layer keeps pointing inwards.

type CapabilityReader

type CapabilityReader interface {
	Execute(context.Context, appshared.ActorContext) (usecase.Capabilities, error)
}

CapabilityReader is the slice of the self-description use case this controller needs.

type Chain

type Chain struct {
	// Routes resolves the route template. It is the router at the end of the chain.
	Routes Router
	// Entry is the outermost middleware; serving starts there and ends at Routes.
	Entry http.Handler
}

Chain is a Router whose requests travel through middleware before they reach the router.

It exists because the two ends want different things: the observability wrapper needs the route template, which only the router knows and only before dispatch, while auth, limits and idempotency need to run between the two. Chain keeps the question and the answer apart - Routes is asked for the template, Handler is what actually serves.

func (Chain) Handler

func (c Chain) Handler(r *http.Request) (http.Handler, string)

func (Chain) ServeHTTP

func (c Chain) ServeHTTP(w http.ResponseWriter, r *http.Request)

type Changes

type Changes interface {
	Resume(ctx context.Context, actor appshared.ActorContext, cursor string) (
		syncservice.Position, error)
	Next(ctx context.Context, actor appshared.ActorContext, from syncservice.Position) (
		syncservice.Batch, error)
	Encode(position syncservice.Position) string
}

Changes is the slice of the stream service this controller drives: where a client resumes, what it may see next, and how a position is spelled on the wire.

An interface rather than the service itself, for the reason UseCaseRegistry is one - this is the whole of what the transport is allowed to do with the stream, and a controller test needs no database to prove framing.

type Decision

type Decision struct {
	Allowed   bool
	Limit     int
	Remaining int
	// RetryAfter is how long until one token is available again. Zero when the request was
	// allowed.
	RetryAfter time.Duration
	// Reset is how long until the bucket is full again.
	Reset time.Duration
}

Decision is what the caller needs to answer with, whether or not the request is allowed: the RateLimit headers belong on every response, not only on a 429 (api-guidelines.md §5).

type Fallback

type Fallback struct {
	// API resolves the route template of a request the API owns. It is the router, not the chain.
	API Router
	// Reserved are the paths the API owns. An entry ending in "/" is a prefix; anything else is
	// matched exactly.
	Reserved []string
	// Serve is the API's middleware chain - authentication, limits, idempotency, and the router
	// at the end of it.
	Serve http.Handler
	// UI serves the embedded bundle. Nil when the interface is switched off, and then every path
	// the API does not own is answered 404 here.
	UI http.Handler
}

Fallback decides, for every request, whether the API owns the path or the user interface does.

The rule is one sentence and the order in it is the whole point: the API keeps every path it owns, and the user interface gets what is left (ADR-0028). A route the specification declares can therefore never be shadowed by a file that happens to have the same name, and a request under /api/ that matches no route still gets the API's own problem document rather than an HTML page a client cannot parse.

It sits directly under the observability wrapper and above everything else, which is what keeps the user interface out of the API's middleware: a static file needs no actor, no tenant and no idempotency key, and a page load that spent six requests of the anonymous rate limit budget would make the first visit the last one.

func (Fallback) Handler

func (f Fallback) Handler(r *http.Request) (http.Handler, string)

func (Fallback) ServeHTTP

func (f Fallback) ServeHTTP(w http.ResponseWriter, r *http.Request)

type HealthReportReader

type HealthReportReader interface {
	Execute(context.Context, appshared.ActorContext) (health.Report, error)
}

HealthReportReader is the slice of the use case this controller needs.

type IdempotencyGuard

IdempotencyGuard is the slice of the guard use case this middleware needs.

type Idempotent

type Idempotent struct {
	Next  http.Handler
	Guard IdempotencyGuard
	// Routes resolves the route template, which is the endpoint half of the key. The template
	// rather than the path: two items are two requests, but the same key sent to two different
	// operations must not share a record.
	Routes Router
}

Idempotent executes a keyed write once and replays its answer for every repeat.

Only POST. The other write methods are idempotent by their own definition - PUT and DELETE repeat harmlessly, and PATCH is guarded by If-Match instead (api-guidelines.md §5).

func (Idempotent) ServeHTTP

func (i Idempotent) ServeHTTP(w http.ResponseWriter, r *http.Request)

type InboundRunStarter

type InboundRunStarter interface {
	Execute(
		ctx context.Context, delivery automation.InboundDelivery,
	) (automation.TriggerRuleResult, error)
}

InboundRunStarter is the slice of the inbound path this controller needs. An interface rather than the service, so the route can be tested without a database and the presentation layer keeps pointing inwards.

type JumbleIntakeDeliverer

type JumbleIntakeDeliverer interface {
	Deliver(ctx context.Context, presented, sender, subject, body string) (jumbledomain.Entry, error)
}

JumbleIntakeDeliverer serves the public intake route: not a catalogue entry, because it answers a credential nobody in this system holds. Nil leaves the route answering the pending 404.

type LimitSignals

type LimitSignals interface {
	RateLimited(ctx context.Context, scope string)
}

Limited refuses a request that has spent its budget.

One type, wired more than once: before authentication it counts per credential or per client address, afterwards per tenant (security.md §9, "multi-level"). Counting per credential needs no database - the key is a fingerprint of the presented string - which is what keeps a flood of invalid tokens from costing a lookup each. LimitSignals is the metric slice a refusal reports through - the scope is the level, so an operator sees which wall is being hit (observability-reliability.md §4).

type Limited

type Limited struct {
	// Signals is optional; nil records nothing.
	Signals LimitSignals
	Next    http.Handler
	Limiter *RateLimiter
	// Level names the bucket in the refusal, so an operator reading a 429 knows which limit bit.
	// It also separates the key spaces, so one address cannot spend a tenant's budget.
	Level string
	// Bucket decides which budget this request counts against, and how large it is.
	Bucket func(*http.Request) Bucket
	// Clock is injectable so the tests do not have to sleep. Nil means the system clock.
	Clock func() time.Time
}

func (Limited) ServeHTTP

func (l Limited) ServeHTTP(w http.ResponseWriter, r *http.Request)

type Localised

type Localised struct {
	Next   http.Handler
	Locale env.LocaleConfig
	// Negotiator reads Accept-Language against the catalogues the installation has: it is
	// the i18n adapter, because the matching is a library's and this layer may not import it
	// (ADR-0056). Nil negotiates nothing, which is the installation default for everybody.
	Negotiator i18n.Negotiator
}

Localised resolves the language and the time zone of the request and puts an actor into the context carrying them.

It runs before authentication, because an unauthenticated answer needs a language too - a 401 is rendered by the client from a code and its parameters, and the client needs to know which language it was asked for. Authentication then replaces the anonymous actor with the real one, putting the account's own preference first where there is one (i18n-l10n.md §2).

func (Localised) ServeHTTP

func (l Localised) ServeHTTP(w http.ResponseWriter, r *http.Request)

type MailDeliverer

type MailDeliverer interface {
	Deliver(ctx context.Context, presented string, raw []byte) (jumbledomain.Entry, error)
}

MailDeliverer serves the mail route: the message as it arrived, and the entry it became. Not a catalogue entry either, for the webhook door's reason - it answers a credential nobody in this system holds. Nil leaves the route answering the pending 404.

type MediaContentService

type MediaContentService interface {
	Receive(ctx context.Context, grant usecasemedia.Grant, content io.Reader) error
	Send(ctx context.Context, grant usecasemedia.Grant) (usecasemedia.Served, error)
}

MediaContentService moves the bytes once the capability has been verified.

type MediaTokenValidator

type MediaTokenValidator interface {
	ValidateUpload(token string, mediaID shared.ID, now time.Time) (shared.ID, error)
	ValidateDownload(token string, mediaID shared.ID, now time.Time) (shared.ID, error)
}

MediaTokenValidator judges a content-route capability and answers which tenant it was minted in.

An interface here rather than the issuer itself, because presentation may not import infrastructure - and because this is the whole of what this layer does with a media token: it verifies a signature, which is authentication, and decides nothing else (rule 2, ADR-0005).

type MetricRecorder

type MetricRecorder interface {
	HTTPRequest(ctx context.Context, route, method string, status int, seconds float64)
	InflightDelta(ctx context.Context, role string, delta int64)
}

MetricRecorder is the slice of the metrics adapter this middleware uses. An interface rather than the adapter, so the presentation layer keeps pointing inwards (project-structure.md §2).

type Mounted

type Mounted struct {
	// Router is the specification's routes.
	Router Router
	// Path is the mounted path, matched exactly - or, with Prefix set, everything beneath it.
	Path string
	// Prefix mounts a whole tree rather than one path: the CalDAV tree is a WebDAV
	// hierarchy under one root, and every address in it is answered by one handler and counted
	// under one route label, because a metric label per calendar would be one per feed.
	Prefix bool
	// Mount answers it.
	Mount http.Handler
}

Mounted serves one path that the specification does not describe, and leaves everything else to the router that it does.

There is exactly one such path today: /mcp. MCP is not REST - it is JSON-RPC over one endpoint (ai-first.md §1.1) - so it has no place in api/openapi.yaml, and registering it on the Mux would put a route into the contract test that the contract does not contain. Mounting it here keeps both true: the specification still describes every REST route, and the MCP endpoint still travels through the same middleware, so it is authenticated, rate limited and observed like everything else.

func (Mounted) Handler

func (m Mounted) Handler(r *http.Request) (http.Handler, string)

Handler resolves the request, returning the mounted path as its own route template so that a metric label and a span name exist for it too (observability-reliability.md §3.2).

func (Mounted) ServeHTTP

func (m Mounted) ServeHTTP(w http.ResponseWriter, r *http.Request)

type Mux

type Mux struct {
	// contains filtered or unexported fields
}

Mux is the router the generated code registers its routes on.

It exists for one reason: the specification writes an action as a suffix of the resource (`POST /items/{itemId}:complete`, Google AIP style, api-guidelines.md §2), and net/http's router requires a wildcard to span a whole path segment - `{itemId}:complete` makes it panic at registration. So the colon gets a segment of its own on the way in, and requests are rewritten the same way on the way out. The contract keeps its form, the router gets one it can parse, and no third-party router enters the dependency list for a one-character difference.

It also answers the two conditions the router itself decides - no such route, and not this method - as RFC 9457 problems rather than as net/http's plain text (security.md §9).

func NewMux

func NewMux() *Mux

func (*Mux) HandleFunc

func (m *Mux) HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request))

HandleFunc implements the ServeMux interface of the generated code, which is the seam that lets this rewrite happen without touching a generated line (rule 11).

func (*Mux) Handler

func (m *Mux) Handler(r *http.Request) (http.Handler, string)

Handler resolves a request to its handler and to the specification's route template, without serving. It is what the observability middleware asks before dispatch.

func (*Mux) Routes

func (m *Mux) Routes() []string

Routes returns the route templates as the specification writes them, in registration order.

func (*Mux) ServeHTTP

func (m *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request)

type Observed

type Observed struct {
	// Router is served, and asked for the route template first.
	Router Router
	// Metrics may be nil in a test that only cares about the response.
	Metrics MetricRecorder
	// Tracer is never nil in practice: with tracing off it is the no-op tracer, which still
	// carries an incoming trace context through (§3.3).
	Tracer trace.Tracer
	// Role labels the inflight gauge. One process can serve several roles (ADR-0014); this is
	// the one whose port is being served.
	Role string
}

Observed is the RED middleware of §4: a rate, an error class, and a duration for every request, plus the span and the request ID everything else correlates against.

It is also the outermost panic guard of the request path. A handler that panics still owes the client an answer, and it must be an RFC 9457 problem rather than a dropped connection.

func (Observed) ServeHTTP

func (o Observed) ServeHTTP(w http.ResponseWriter, r *http.Request)

type OpsController

type OpsController struct {
	Health health.Registry
	// Metrics is the Prometheus endpoint. It lives here rather than on the public port
	// deliberately: the series say what runs, how much of it, and how slowly
	// (observability-reliability.md §3.2).
	Metrics http.Handler
}

func (OpsController) Routes

func (c OpsController) Routes() http.Handler

type Problem

type Problem struct {
	Type        string            `json:"type"`
	Title       string            `json:"title"`
	Status      int               `json:"status"`
	Code        string            `json:"code"`
	DetailCode  string            `json:"detail_code,omitempty"`
	Params      map[string]string `json:"params,omitempty"`
	FieldErrors []ProblemField    `json:"field_errors,omitempty"`
	RequestID   string            `json:"request_id,omitempty"`
	Docs        string            `json:"docs"`
}

Problem is the error format from api-guidelines.md §6. The field names follow the schema in api/openapi.yaml; changing one is a breaking change (versioning-release.md §2).

What is deliberately absent: any free text. There is no `detail` field with a sentence in it, because the server produces no display text (ADR-0011) - the client builds the sentence from DetailCode and Params.

func ProblemFrom

func ProblemFrom(err error, requestID string) Problem

ProblemFrom maps any error to problem details. An error that is not a domain error becomes INTERNAL, and nothing of its text reaches the response - it may contain a connection string, a query fragment, or a path (security.md §9).

requestID comes from the request ID middleware and is what a user quotes in a support request: it is the only handle connecting a response to a log entry.

type ProblemField

type ProblemField struct {
	Path   string            `json:"path"`
	Code   string            `json:"code"`
	Params map[string]string `json:"params,omitempty"`
}

ProblemField is one finding on one field. Path is a JSON Pointer (RFC 6901).

type Puller

type Puller interface {
	Pull(ctx context.Context, actor appshared.ActorContext, request syncservice.PullRequest) (
		syncservice.Page, error)
	Encode(position syncservice.Position) string
}

Puller is the slice of the pull service this controller drives: one page in, one page out, and how a position is spelled on the wire. The same reason StreamController holds an interface - this is the whole of what the transport may do with the log, and a controller test needs no database to prove the mapping.

type PushSignals

type PushSignals interface {
	PushResult(ctx context.Context, result string)
}

PushSignals counts what a push did, by result: the closed set the contract names, never an identifier.

type Pusher

type Pusher interface {
	Push(ctx context.Context, actor appshared.ActorContext, request syncservice.PushRequest) (
		syncservice.PushResponse, error)
	Encode(position syncservice.Position) string
}

Pusher is the slice of the push service this controller drives.

type RateLimiter

type RateLimiter struct {
	// contains filtered or unexported fields
}

RateLimiter is a token bucket per key.

Per process, deliberately. A shared counter in PostgreSQL or Redis would be exact across replicas and would put a write on the hot path of every request, including the requests it is meant to refuse - which is how a rate limiter becomes the outage it was installed to prevent. The effective limit is therefore the configured one times the number of replicas, and that is the documented trade-off (observability-reliability.md §6).

func NewRateLimiter

func NewRateLimiter() *RateLimiter

func (*RateLimiter) Allow

func (l *RateLimiter) Allow(key string, limit, burst int, now time.Time) Decision

Allow spends one token from the key's bucket.

burst is the depth of the bucket: how much of a minute's budget may be spent at once. A limit without one would force every client into an even cadence, which no real client has.

type RestController

type RestController struct {

	// Capabilities answers /meta/capabilities. One field per use case as they land; nil means the
	// operation falls back to the pending answer rather than panicking.
	Capabilities CapabilityReader

	// HealthReport answers /api/v1/meta/health. A typed field rather than a call
	// through the catalogue, although the use case is registered in it: the wire format of this
	// report is hand-written and validated against the schema by a contract test, and it
	// distinguishes "no latency measured" from "zero milliseconds" with a pointer. Carrying it
	// through `usecase.Output`'s map would lose exactly those distinctions on the one route whose
	// readers - a status page, an operator, a support thread - are the people who need them.
	HealthReport HealthReportReader

	// UseCases is the catalogue every business operation goes through. One field rather than one
	// per use case: the operations that arrive from here on are entries in it, not new
	// dependencies of this controller (arc42 §4).
	UseCases UseCaseRegistry

	// MediaContent and MediaTokens serve the two content routes, which are not catalogue entries:
	// they take a stream and answer a stream, which is neither what MCP nor what an automation
	// rule could do with them (core/application/service/media.MediaContent).
	MediaContent MediaContentService
	MediaTokens  MediaTokenValidator
	// Clock judges an expiring capability. The one place this layer needs the time, and a port
	// rather than time.Now so that a test can stand at the moment a token expires.
	Clock clock.Clock

	// BaseURL is this installation's public address, and the only thing this controller composes
	// rather than maps: a calendar feed is handed to a client as a URL to subscribe to, and a URL
	// is a fact about the installation rather than about the domain. Empty answers a
	// relative address, which is what a client that just called this server can still use.
	BaseURL string

	// BaseHost is the installation's own host name, for reading a tenant subdomain off a
	// multi-mode sign-in request (multi-tenancy.md §3). Empty means no subdomain is ever
	// read, which is single mode's and localhost's answer.
	BaseHost string

	// CalendarFeeds serves the public .ics route, which is not a catalogue entry either: it
	// answers a credential nobody in this system holds, and there is nothing for MCP or an
	// automation rule to call. Nil leaves the route answering the pending 404.
	CalendarFeeds CalendarFeedReader

	// InboundRuns serves the public inbound-webhook route, which is not a catalogue entry either:
	// it answers a credential nobody in this system holds, and there is nothing for MCP or an
	// automation rule to call. Nil leaves the route answering the pending 404.
	InboundRuns InboundRunStarter

	// JumbleIntake serves the jumble's public webhook door, for the same reasons.
	JumbleIntake JumbleIntakeDeliverer

	// MailIntake serves the mail door beside it: the same credential, and a body that is
	// the message itself rather than a shape somebody had to agree on.
	MailIntake MailDeliverer
	// AddressedMail serves the receiving domain's inbound route (ADR-0092). Nil leaves it answering
	// the pending 404.
	AddressedMail AddressedMailDeliverer
	// Alexa serves the voice assistant's endpoint (ADR-0093). Nil leaves it answering the pending
	// 404.
	Alexa AlexaAnswerer

	// Stream serves `GET /stream`, which is not a catalogue entry either: it is a connection being
	// held rather than an operation being invoked, so there is nothing for MCP or an automation
	// rule to call. Nil leaves the route answering the pending 404, which is what an
	// installation built without it should say.
	Stream *StreamController

	// Sync serves `POST /sync:pull`, which is not a catalogue entry for the stream's reason: a
	// pull is the stream served in pages. Nil leaves the route answering the pending 404.
	Sync *SyncController
	// contains filtered or unexported fields
}

RestController implements the server interface generated from api/openapi.yaml.

It holds no business logic. An operation reads the request, calls one use case, and maps the result - the rules live in the application layer, and the authorisation check lives there and nowhere else (ADR-0005, project-structure.md §2).

Everything not overridden here comes from the embedded pending set and answers 404: the route exists because the specification declares it, the implementation arrives with its own task.

func NewRestController

func NewRestController() *RestController

func (*RestController) AcceptSuggestion

func (c *RestController) AcceptSuggestion(
	w http.ResponseWriter, r *http.Request, suggestionID openapi.SuggestionId,
)

AcceptSuggestion answers POST /suggestions/{id}:accept.

func (*RestController) AcknowledgeAiPosition

func (c *RestController) AcknowledgeAiPosition(w http.ResponseWriter, r *http.Request)

AcknowledgeAiPosition answers POST /privacy/ai-objection:noticed.

func (*RestController) AddComment

AddComment answers POST /items/{itemId}/comments.

func (*RestController) AddLabel

func (c *RestController) AddLabel(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId, labelID openapi.LabelId,
)

AddLabel answers PUT /items/{itemId}/labels/{labelId}.

func (*RestController) AddMember

func (c *RestController) AddMember(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId, accountID openapi.AccountId,
)

AddMember answers PUT /items/{itemId}/members/{accountId}.

func (*RestController) AddOperator

func (c *RestController) AddOperator(w http.ResponseWriter, r *http.Request)

AddOperator answers POST /admin/operators.

func (*RestController) AgreeToTerms

func (c *RestController) AgreeToTerms(w http.ResponseWriter, r *http.Request)

AgreeToTerms answers POST /auth/sessions:agree-terms: the TERMS step (UC-ID-19). Public, for SignIn's reason: the pending credential in the body is what authenticates the call.

func (*RestController) AiClassify

func (c *RestController) AiClassify(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId,
)

func (*RestController) AiGenerateTemplate

func (c *RestController) AiGenerateTemplate(
	w http.ResponseWriter, r *http.Request, _ openapi.AiGenerateTemplateParams,
)

AiGenerateTemplate answers POST /templates:generate.

202 and no body, for the decomposition's reason: the provider has not been asked yet. The description travels as the use case's input and nothing else is read from the body.

func (*RestController) AiSuggestFields

func (c *RestController) AiSuggestFields(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId,
)

The three actions automation.md §1.3 documents, served over REST as well because an automation action is a use case and a use case is reachable through all three channels.

func (*RestController) AiSummarize

func (c *RestController) AiSummarize(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId,
)

func (*RestController) AiSummarizeContainer

func (c *RestController) AiSummarizeContainer(
	w http.ResponseWriter, r *http.Request, containerID openapi.ContainerId,
)

AiSummarizeContainer answers POST /containers/{containerId}:summarize.

Its own handler rather than `askAi`'s, because what it names is a container: the input key differs, and a body asking for the answer to be applied would be asking for something nothing does.

func (*RestController) AiSummarizeThread

func (c *RestController) AiSummarizeThread(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId,
)

AiSummarizeThread answers POST /items/{itemId}:summarize-thread.

func (*RestController) AiTranslate

func (c *RestController) AiTranslate(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId,
)

AiTranslate answers POST /items/{itemId}:translate: a read, answered now, stored nowhere.

func (*RestController) AnswerAlexa

func (c *RestController) AnswerAlexa(w http.ResponseWriter, r *http.Request)

AnswerAlexa answers POST /voice/alexa.

A request that is not Alexa's, or not for this installation, answers the pending not-found an unwired route answers: a forger learns nothing about which check failed.

func (*RestController) ArchiveContainer

func (c *RestController) ArchiveContainer(
	w http.ResponseWriter, r *http.Request,
	containerID openapi.ContainerId, _ openapi.ArchiveContainerParams,
)

ArchiveContainer answers POST /containers/{containerId}:archive.

No body. The container is named by the path, and there is nothing to decide about archiving it - which is what makes the action suffix right for it: a status field would have to be sent, read and validated to say the one thing this route says by existing (api-guidelines.md §2).

func (*RestController) ArchiveWorkItem

func (c *RestController) ArchiveWorkItem(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId, _ openapi.ArchiveWorkItemParams,
)

ArchiveWorkItem answers POST /items/{itemId}:archive.

No body. The entry is named by the path and there is nothing to decide about archiving it, which is what makes the action suffix right for it: a status field would have to be sent, read and validated to say the one thing this route says by existing (api-guidelines.md §2).

func (*RestController) AskWorkspace

AskWorkspace answers POST /ask: 202 with the job that answers the question.

func (*RestController) AssignWorkItem

func (c *RestController) AssignWorkItem(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId,
	params openapi.AssignWorkItemParams,
)

AssignWorkItem answers POST /items/{itemId}:assign.

func (*RestController) AttachMedia

func (c *RestController) AttachMedia(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId, mediaID openapi.MediaId,
)

AttachMedia answers PUT /items/{itemId}/attachments/{mediaId}.

func (*RestController) AuthorizeMailbox

AuthorizeMailbox answers POST /mail/mailboxes:authorize.

func (*RestController) AuthorizeOauthClient

func (c *RestController) AuthorizeOauthClient(w http.ResponseWriter, r *http.Request)

AuthorizeOauthClient answers POST /oauth/authorize. Written out for ListServiceAccounts' reason: the identity helper's closure gives the linter nothing to trace the context through.

func (*RestController) AutoAssignWorkItem

func (c *RestController) AutoAssignWorkItem(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId,
	params openapi.AutoAssignWorkItemParams,
)

AutoAssignWorkItem answers POST /items/{itemId}:auto-assign. No body either: the whole point is that the caller names nobody - the collection's policy does the choosing.

func (*RestController) BulkUpdateWorkItems

func (c *RestController) BulkUpdateWorkItems(
	w http.ResponseWriter, r *http.Request, _ openapi.BulkUpdateWorkItemsParams,
)

BulkUpdateWorkItems answers POST /items:bulk.

HTTP 200 whenever the bulk was carried out, whatever happened inside it: a bulk that half succeeded is not a failed request, and a client reads what happened per operation rather than from one status covering five hundred of them (api-guidelines.md §5).

func (*RestController) CancelInstanceIdentityProviderWithdrawal

func (c *RestController) CancelInstanceIdentityProviderWithdrawal(
	w http.ResponseWriter, r *http.Request, providerID openapi.ProviderId,
	params openapi.CancelInstanceIdentityProviderWithdrawalParams,
)

CancelInstanceIdentityProviderWithdrawal answers POST /admin/identity-providers/{providerId}:cancel-withdrawal.

func (*RestController) CancelJob

CancelJob answers POST /jobs/{jobId}:cancel.

200 with the job rather than 204, because what a caller wants next is the state it is now in - and because a cancellation is cooperative: the answer says CANCELLED, and the pass that was under way is still winding itself up.

func (*RestController) CancelTenantDeletion

func (c *RestController) CancelTenantDeletion(
	w http.ResponseWriter, r *http.Request, tenantID openapi.AdminTenantId,
)

CancelTenantDeletion answers POST /admin/tenants/{tenantId}:cancel-deletion.

func (*RestController) ChangeInstallationPreset

func (c *RestController) ChangeInstallationPreset(
	w http.ResponseWriter, r *http.Request, presetID openapi.PresetId, params openapi.ChangeInstallationPresetParams,
)

ChangeInstallationPreset answers PATCH /admin/presets/{presetId}.

func (*RestController) ChangePassword

func (c *RestController) ChangePassword(
	w http.ResponseWriter, r *http.Request, params openapi.ChangePasswordParams,
)

ChangePassword answers POST /auth/password.

func (*RestController) ChangePlan

func (c *RestController) ChangePlan(
	w http.ResponseWriter, r *http.Request, planName string, params openapi.ChangePlanParams,
)

ChangePlan answers PUT /admin/plans/{planName}.

func (*RestController) ChangePreset

func (c *RestController) ChangePreset(
	w http.ResponseWriter, r *http.Request, presetID openapi.PresetId, params openapi.ChangePresetParams,
)

ChangePreset answers PATCH /presets/{presetId}.

func (*RestController) CheckMailbox

func (c *RestController) CheckMailbox(
	w http.ResponseWriter, r *http.Request, mailboxID openapi_types.UUID, _ openapi.CheckMailboxParams,
)

CheckMailbox answers POST /mail/mailboxes/{mailbox_id}:check.

func (*RestController) CheckPassword

func (c *RestController) CheckPassword(
	w http.ResponseWriter, r *http.Request, params openapi.CheckPasswordParams,
)

CheckPassword answers POST /auth/password:check.

Written out rather than through the identity helper: the route is public, because three of the four proofs it accepts are the tokens of a flow that has no bearer yet.

func (*RestController) CheckRules

func (c *RestController) CheckRules(w http.ResponseWriter, r *http.Request)

CheckRules answers POST /automation/rules:check (ADR-0060).

func (*RestController) ChooseAiSource

func (c *RestController) ChooseAiSource(w http.ResponseWriter, r *http.Request)

ChooseAiSource answers PUT /ai-source.

func (*RestController) ClearCover

func (c *RestController) ClearCover(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId, params openapi.ClearCoverParams,
)

ClearCover answers DELETE /items/{itemId}/cover. No body: taking the cover off names nothing beyond which entry.

func (*RestController) ClearDueDate

func (c *RestController) ClearDueDate(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId, params openapi.ClearDueDateParams,
)

ClearDueDate answers DELETE /items/{itemId}/due. No body: taking the due date off names nothing beyond which entry - the instant, the flag and the zone leave together.

func (*RestController) CloseStarter

func (c *RestController) CloseStarter(w http.ResponseWriter, r *http.Request)

CloseStarter answers POST /tenant/starter:close.

func (*RestController) CloseTenantPassword

func (c *RestController) CloseTenantPassword(
	w http.ResponseWriter, r *http.Request, tenantID openapi.AdminTenantId,
)

CloseTenantPassword answers POST /admin/tenants/{tenantId}:close-password. Written out longhand for the same reason as its siblings above.

func (*RestController) CommentJumbleEntry

func (c *RestController) CommentJumbleEntry(
	w http.ResponseWriter, r *http.Request, entryID openapi_types.UUID,
	_ openapi.CommentJumbleEntryParams,
)

CommentJumbleEntry answers POST /jumble/entries/{entryId}:comment.

func (c *RestController) CompleteLink(w http.ResponseWriter, r *http.Request)

CompleteLink answers POST /auth/sessions:link: the account's password before a provider is connected to it (ADR-0071's addendum). A pair, or the second factor's step with the link carried.

func (*RestController) CompleteMailboxAuthorization

func (c *RestController) CompleteMailboxAuthorization(
	w http.ResponseWriter, r *http.Request, _ openapi.CompleteMailboxAuthorizationParams,
)

CompleteMailboxAuthorization answers POST /mail/mailboxes:complete.

func (*RestController) CompleteOidcSignIn

func (c *RestController) CompleteOidcSignIn(w http.ResponseWriter, r *http.Request)

CompleteOidcSignIn answers POST /auth/oidc:callback.

func (*RestController) CompletePasskeyEnrollment

func (c *RestController) CompletePasskeyEnrollment(w http.ResponseWriter, r *http.Request)

CompletePasskeyEnrollment answers POST /auth/mfa/passkey:enroll: the key kept, and the sign-in's pair or the step it still owes.

func (*RestController) CompletePasskeyRegistration

func (c *RestController) CompletePasskeyRegistration(w http.ResponseWriter, r *http.Request)

CompletePasskeyRegistration answers POST /accounts/me/passkeys:complete.

func (*RestController) CompletePasskeySignIn

func (c *RestController) CompletePasskeySignIn(w http.ResponseWriter, r *http.Request)

CompletePasskeySignIn answers POST /auth/sessions:passkey.

func (*RestController) CompleteProviderConnect

func (c *RestController) CompleteProviderConnect(w http.ResponseWriter, r *http.Request)

CompleteProviderConnect answers POST /accounts/me/identities:complete.

Written out rather than through the `identity` helper, GetOwnAccount's reasoning: without a path parameter the helper's closure hides the request from `contextcheck`.

func (*RestController) CompleteSignIn

func (c *RestController) CompleteSignIn(w http.ResponseWriter, r *http.Request)

CompleteSignIn answers POST /auth/sessions:verify. Written out for SignIn's reason: the route is public, and the pending credential in the body is what authenticates the call.

func (*RestController) CompleteWorkItem

func (c *RestController) CompleteWorkItem(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId, _ openapi.CompleteWorkItemParams,
)

CompleteWorkItem answers POST /items/{itemId}:complete.

The body is optional in the contract and carries one field. It is decoded when it is there and its absence is not an error - which is why this does not go through decodeJSON, whose job is to refuse a body that is malformed rather than to tolerate one that is missing.

func (*RestController) ConfigureAiProvider

func (c *RestController) ConfigureAiProvider(w http.ResponseWriter, r *http.Request)

ConfigureAiProvider answers PUT /ai-provider.

func (*RestController) ConfigureAuditAnchoring

func (c *RestController) ConfigureAuditAnchoring(w http.ResponseWriter, r *http.Request)

ConfigureAuditAnchoring answers PUT /audit/anchoring.

func (*RestController) ConfigureFirstIdentityProvider

func (c *RestController) ConfigureFirstIdentityProvider(
	w http.ResponseWriter, r *http.Request, params openapi.ConfigureFirstIdentityProviderParams,
)

ConfigureFirstIdentityProvider answers PUT /identity-provider.

func (*RestController) ConfigureIdentityProvider

func (c *RestController) ConfigureIdentityProvider(
	w http.ResponseWriter, r *http.Request, providerID openapi.ProviderId,
	params openapi.ConfigureIdentityProviderParams,
)

ConfigureIdentityProvider answers PUT /identity-providers/{providerId}.

func (*RestController) ConfigureInstanceAiProvider

func (c *RestController) ConfigureInstanceAiProvider(
	w http.ResponseWriter, r *http.Request, providerID openapi.ProviderId,
)

ConfigureInstanceAiProvider answers PUT /admin/ai-providers/{providerId}.

func (*RestController) ConfigureInstanceIdentityProvider

func (c *RestController) ConfigureInstanceIdentityProvider(
	w http.ResponseWriter, r *http.Request, providerID openapi.ProviderId,
	params openapi.ConfigureInstanceIdentityProviderParams,
)

ConfigureInstanceIdentityProvider answers PUT /admin/identity-providers/{providerId}.

func (*RestController) ConfirmAddress

func (c *RestController) ConfirmAddress(w http.ResponseWriter, r *http.Request)

ConfirmAddress answers POST /auth/address:confirm: the link mailed to a managed account's new address. Public, the reset's reasoning; it signs nobody in, so the answer has no body.

func (*RestController) ConfirmAuthenticatorReplacement

func (c *RestController) ConfirmAuthenticatorReplacement(w http.ResponseWriter, r *http.Request)

ConfirmAuthenticatorReplacement answers POST /auth/mfa/totp/replacement:confirm.

func (*RestController) ConfirmMediaUpload

func (c *RestController) ConfirmMediaUpload(
	w http.ResponseWriter, r *http.Request, mediaID openapi.MediaId, _ openapi.ConfirmMediaUploadParams,
)

ConfirmMediaUpload answers POST /media/{mediaId}:confirm.

func (*RestController) ConfirmTotp

func (c *RestController) ConfirmTotp(w http.ResponseWriter, r *http.Request)

ConfirmTotp answers POST /auth/mfa/totp:confirm.

func (*RestController) ConnectMailbox

ConnectMailbox answers POST /mail/mailboxes.

func (*RestController) ConvertJumbleEntry

func (c *RestController) ConvertJumbleEntry(
	w http.ResponseWriter, r *http.Request, entryID openapi_types.UUID,
	_ openapi.ConvertJumbleEntryParams,
)

ConvertJumbleEntry answers POST /jumble/entries/{entryId}:convert.

func (*RestController) CountAccountsWithoutProvider

func (c *RestController) CountAccountsWithoutProvider(w http.ResponseWriter, r *http.Request)

CountAccountsWithoutProvider answers GET /tenant/accounts-without-provider: the number the password switch says before the password is switched off (ADR-0078 §1).

func (*RestController) CreateAccessToken

func (c *RestController) CreateAccessToken(
	w http.ResponseWriter, r *http.Request, params openapi.CreateAccessTokenParams,
)

CreateAccessToken answers POST /auth/tokens.

func (*RestController) CreateBackupSchedule

func (c *RestController) CreateBackupSchedule(w http.ResponseWriter, r *http.Request)

CreateBackupSchedule answers POST /backup-schedules.

func (*RestController) CreateBackupTarget

func (c *RestController) CreateBackupTarget(w http.ResponseWriter, r *http.Request)

CreateBackupTarget answers POST /backup-targets.

func (*RestController) CreateBucket

func (c *RestController) CreateBucket(
	w http.ResponseWriter, r *http.Request, containerID openapi.ContainerId,
)

CreateBucket answers POST /containers/{containerId}/buckets.

func (*RestController) CreateCalendarFeed

func (c *RestController) CreateCalendarFeed(
	w http.ResponseWriter, r *http.Request, _ openapi.CreateCalendarFeedParams,
)

CreateCalendarFeed answers POST /integrations/calendar-feeds.

func (*RestController) CreateContainer

CreateContainer answers POST /containers.

The handler holds no rules. It reads the request, hands it to the catalogue, and maps the result - the permission check, the invariants, the event, the change log entry and the audit entry all happen in the application layer, once, for every channel (ADR-0005, arc42 §4).

It goes through the catalogue rather than calling the use case directly, which is what makes this identical to the same operation reached through MCP or from an automation rule: one handler, one audit entry, one metric, whichever door the call came in by (test AT-6).

func (*RestController) CreateDataSubjectRequest

func (c *RestController) CreateDataSubjectRequest(
	w http.ResponseWriter, r *http.Request, _ openapi.CreateDataSubjectRequestParams,
)

CreateDataSubjectRequest answers POST /privacy/requests.

func (*RestController) CreateGroup

CreateGroup answers POST /groups.

func (*RestController) CreateIdentityProvider

func (c *RestController) CreateIdentityProvider(
	w http.ResponseWriter, r *http.Request, params openapi.CreateIdentityProviderParams,
)

CreateIdentityProvider answers POST /identity-providers.

func (*RestController) CreateInstallationPreset

func (c *RestController) CreateInstallationPreset(w http.ResponseWriter, r *http.Request)

CreateInstallationPreset answers POST /admin/presets.

func (*RestController) CreateInstanceAiProvider

func (c *RestController) CreateInstanceAiProvider(w http.ResponseWriter, r *http.Request)

CreateInstanceAiProvider answers POST /admin/ai-providers.

func (*RestController) CreateInstanceBackupTarget

func (c *RestController) CreateInstanceBackupTarget(
	w http.ResponseWriter, r *http.Request, params openapi.CreateInstanceBackupTargetParams,
)

CreateInstanceBackupTarget answers POST /admin/backup-targets: the workspace's body, the installation's scope, and the step-up header.

func (*RestController) CreateInstanceIdentityProvider

func (c *RestController) CreateInstanceIdentityProvider(
	w http.ResponseWriter, r *http.Request, params openapi.CreateInstanceIdentityProviderParams,
)

CreateInstanceIdentityProvider answers POST /admin/identity-providers.

func (*RestController) CreateLabel

func (c *RestController) CreateLabel(
	w http.ResponseWriter, r *http.Request, containerID openapi.ContainerId,
)

CreateLabel answers POST /containers/{containerId}/labels.

func (*RestController) CreateManagedAccount

func (c *RestController) CreateManagedAccount(
	w http.ResponseWriter, r *http.Request, params openapi.CreateManagedAccountParams,
)

CreateManagedAccount answers POST /accounts:create-managed.

The answer carries the start password, once: it is marked `no-store` for a cache and as shown once for the idempotency record, so neither keeps it.

func (*RestController) CreatePlan

CreatePlan answers POST /admin/plans.

func (*RestController) CreatePlatformSubscription

func (c *RestController) CreatePlatformSubscription(
	w http.ResponseWriter, r *http.Request, params openapi.CreatePlatformSubscriptionParams,
)

CreatePlatformSubscription answers POST /admin/subscriptions, the step-up in its header.

func (*RestController) CreateReminder

func (c *RestController) CreateReminder(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId,
	_ openapi.CreateReminderParams,
)

CreateReminder answers POST /items/{itemId}/reminders.

func (*RestController) CreateRetentionPolicy

func (c *RestController) CreateRetentionPolicy(w http.ResponseWriter, r *http.Request)

CreateRetentionPolicy answers POST /retention-policies.

func (*RestController) CreateRule

CreateAutomationRule answers POST /automation/rules.

func (*RestController) CreateSavedView

CreateSavedView answers POST /views.

func (*RestController) CreateServiceAccount

func (c *RestController) CreateServiceAccount(
	w http.ResponseWriter, r *http.Request, _ openapi.CreateServiceAccountParams,
)

CreateServiceAccount answers POST /auth/service-accounts.

func (*RestController) CreateTemplate

CreateTemplate answers POST /templates.

func (*RestController) CreateWebhookSubscription

func (c *RestController) CreateWebhookSubscription(
	w http.ResponseWriter, r *http.Request, _ openapi.CreateWebhookSubscriptionParams,
)

CreateWebhookSubscription answers POST /integrations/webhooks.

func (*RestController) CreateWorkItem

CreateWorkItem answers POST /items.

Like CreateContainer, it holds no rules: it reads the request, hands it to the catalogue, and maps the result. Which type may sit under which, and how deep, is decided in the application layer from the capability profiles - so this handler knows nothing about tasks, work packages or activities, and a level configured later needs no change here (ADR-0006, arc42 §4).

func (*RestController) DefineCustomField

func (c *RestController) DefineCustomField(
	w http.ResponseWriter, r *http.Request, _ openapi.DefineCustomFieldParams,
)

DefineCustomField answers POST /custom-fields.

func (*RestController) DeleteBackupSchedule

func (c *RestController) DeleteBackupSchedule(
	w http.ResponseWriter, r *http.Request, scheduleID openapi_types.UUID,
)

DeleteBackupSchedule answers DELETE /backup-schedules/{scheduleId}.

func (*RestController) DeleteBackupTarget

func (c *RestController) DeleteBackupTarget(
	w http.ResponseWriter, r *http.Request, targetID openapi_types.UUID,
)

DeleteBackupTarget answers DELETE /backup-targets/{targetId}.

func (*RestController) DeleteBucket

DeleteBucket answers DELETE /containers/{containerId}/buckets/{bucketId}.

200 with a body rather than 204, unlike a container's deletion. What became of the entries that were in the column is not derivable from the request, and a client that received no content would have to reload the whole board to find out where its cards are.

func (*RestController) DeleteComment

func (c *RestController) DeleteComment(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId, commentID openapi.CommentId,
	params openapi.DeleteCommentParams,
)

DeleteComment answers DELETE /items/{itemId}/comments/{commentId}. 204: the caller asked for the comment to be gone, and what remains - the tombstone - is read through the thread.

func (*RestController) DeleteCustomField

func (c *RestController) DeleteCustomField(
	w http.ResponseWriter, r *http.Request, fieldID openapi.CustomFieldId,
	params openapi.DeleteCustomFieldParams,
)

DeleteCustomField answers DELETE /custom-fields/{fieldId}.

func (*RestController) DeleteGroup

func (c *RestController) DeleteGroup(w http.ResponseWriter, r *http.Request, groupID openapi.GroupId, params openapi.DeleteGroupParams)

DeleteGroup answers DELETE /groups/{groupId}.

func (*RestController) DeleteInstanceBackupTarget

func (c *RestController) DeleteInstanceBackupTarget(
	w http.ResponseWriter, r *http.Request, targetID openapi_types.UUID,
	params openapi.DeleteInstanceBackupTargetParams,
)

DeleteInstanceBackupTarget answers DELETE /admin/backup-targets/{targetId}.

func (*RestController) DeleteLabel

DeleteLabel answers DELETE /containers/{containerId}/labels/{labelId}.

204 rather than the 200 a deleted column answers with. Nothing became of anything that a client could not work out for itself: the entries that carried the label stop showing it, and there is no destination to report.

func (*RestController) DeleteMedia

func (c *RestController) DeleteMedia(w http.ResponseWriter, r *http.Request, mediaID openapi.MediaId)

DeleteMedia answers DELETE /media/{mediaId}.

func (*RestController) DeleteOauthClient

func (c *RestController) DeleteOauthClient(
	w http.ResponseWriter, r *http.Request, clientID openapi.OauthClientId,
)

DeleteOauthClient answers DELETE /oauth/clients/{clientId}.

func (*RestController) DeletePlatformSubscription

func (c *RestController) DeletePlatformSubscription(
	w http.ResponseWriter, r *http.Request, subscriptionID openapi_types.UUID,
)

DeletePlatformSubscription answers DELETE /admin/subscriptions/{subscriptionId}.

func (*RestController) DeleteReminder

func (c *RestController) DeleteReminder(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId,
	reminderID openapi.ReminderId, params openapi.DeleteReminderParams,
)

DeleteReminder answers DELETE /items/{itemId}/reminders/{reminderId}. 204: the caller asked for the reminder to be gone, and it is - there is no tombstone to read.

func (*RestController) DeleteRetentionPolicy

func (c *RestController) DeleteRetentionPolicy(
	w http.ResponseWriter, r *http.Request, policyID openapi_types.UUID,
)

DeleteRetentionPolicy answers DELETE /retention-policies/{policyId}.

func (*RestController) DeleteRule

func (c *RestController) DeleteRule(
	w http.ResponseWriter, r *http.Request, ruleID openapi.RuleId,
)

DeleteAutomationRule answers DELETE /automation/rules/{ruleId}.

func (*RestController) DeleteSavedView

func (c *RestController) DeleteSavedView(
	w http.ResponseWriter, r *http.Request, viewID openapi.ViewId, params openapi.DeleteSavedViewParams,
)

DeleteSavedView answers DELETE /views/{viewId}.

func (*RestController) DeleteTemplate

func (c *RestController) DeleteTemplate(
	w http.ResponseWriter, r *http.Request, templateID openapi.TemplateId,
	params openapi.DeleteTemplateParams,
)

DeleteTemplate answers DELETE /templates/{templateId}. 204: the caller asked for the template to be gone, and the trees it stamped out are read as the entries they are.

func (*RestController) DeleteWebhookSubscription

func (c *RestController) DeleteWebhookSubscription(
	w http.ResponseWriter, r *http.Request, webhookID openapi.WebhookId,
)

DeleteWebhookSubscription answers DELETE /integrations/webhooks/{webhookId}.

func (*RestController) DeliverAddressedMail

func (c *RestController) DeliverAddressedMail(
	w http.ResponseWriter, r *http.Request, params openapi.DeliverAddressedMailParams,
)

DeliverAddressedMail answers POST /mail/inbound.

The body is read whole, the mail door's way: the request middleware bounds it before a byte is read. A missing or wrong key answers the same not-found an unknown address gets, so the route tells a stranger nothing about whether it exists.

func (*RestController) DeliverMail

func (c *RestController) DeliverMail(w http.ResponseWriter, r *http.Request, presented string)

DeliverMail answers POST /jumble/mail/{token}.

The body is read whole rather than streamed, and that is what the bound is for: the request middleware refuses a payload over the mail limit before a byte of it is read, so what reaches this is a message of a size this installation agreed to take (T-17). Reading it whole is what the parser needs - MIME is walked, not consumed - and it is the same shape a mail spool has.

func (*RestController) DescribeRule

DescribeRule answers POST /automation/rules:describe: 202 with the job that drafts the rule.

func (*RestController) DetachMedia

func (c *RestController) DetachMedia(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId, mediaID openapi.MediaId,
)

DetachMedia answers DELETE /items/{itemId}/attachments/{mediaId}.

func (*RestController) DisableRule

DisableAutomationRule answers POST /automation/rules/{ruleId}:disable.

func (*RestController) DisableTotp

func (c *RestController) DisableTotp(
	w http.ResponseWriter, r *http.Request, params openapi.DisableTotpParams,
)

DisableTotp answers POST /auth/mfa:disable. Written out for ListServiceAccounts' reason: the identity helper's closure gives the linter nothing to trace the request's context through.

func (*RestController) DismissJumbleEntry

func (c *RestController) DismissJumbleEntry(
	w http.ResponseWriter, r *http.Request, entryID openapi_types.UUID,
	_ openapi.DismissJumbleEntryParams,
)

DismissJumbleEntry answers POST /jumble/entries/{entryId}:dismiss.

func (*RestController) DismissSuggestion

func (c *RestController) DismissSuggestion(
	w http.ResponseWriter, r *http.Request, suggestionID openapi.SuggestionId,
)

DismissSuggestion answers POST /suggestions/{id}:dismiss.

func (*RestController) DownloadMediaContent

func (c *RestController) DownloadMediaContent(
	w http.ResponseWriter, r *http.Request, mediaID openapi.MediaId, params openapi.DownloadMediaContentParams,
)

DownloadMediaContent answers GET /media/{mediaId}:content.

A download and never a rendering path: the disposition is attachment, the type is the sniffed one rather than anything the uploader claimed, and the sandboxing policy is set on the answer itself - so a browser that follows this URL saves a file rather than executing one (T-11).

func (*RestController) DuplicateWorkItem

func (c *RestController) DuplicateWorkItem(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId, _ openapi.DuplicateWorkItemParams,
)

DuplicateWorkItem answers POST /items/{itemId}:duplicate.

func (*RestController) EditComment

func (c *RestController) EditComment(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId, commentID openapi.CommentId,
	params openapi.EditCommentParams,
)

EditComment answers PATCH /items/{itemId}/comments/{commentId}.

func (*RestController) ElevateSession

func (c *RestController) ElevateSession(
	w http.ResponseWriter, r *http.Request, params openapi.ElevateSessionParams,
)

ElevateSession answers POST /auth/sessions:elevate.

func (*RestController) EmptyTrash

EmptyTrash answers POST /trash:empty.

200 with a summary rather than 204, because a pass is not necessarily the whole of it: a large trash takes several, and a client that got no answer could not tell that it should call again - nor that three rows stayed behind under a legal hold.

func (*RestController) EnableRule

EnableAutomationRule answers POST /automation/rules/{ruleId}:enable.

func (*RestController) EnrollTotp

func (c *RestController) EnrollTotp(w http.ResponseWriter, r *http.Request)

EnrollTotp answers POST /auth/mfa/totp:enroll. Public for the enforcement flow; a signed-in caller's bearer was verified by the middleware exactly as on any public route.

func (*RestController) ExchangeOauthCode

func (c *RestController) ExchangeOauthCode(w http.ResponseWriter, r *http.Request)

ExchangeOauthCode answers POST /oauth/token. Written out for SignIn's reason: the route is public, and the body is the whole of what authenticates.

func (*RestController) ExportAuditTrail

func (c *RestController) ExportAuditTrail(w http.ResponseWriter, r *http.Request)

ExportAuditTrail answers POST /audit:export.

A `202` with the job to poll. The archive lands at the backup target the caller named rather than at a URL this server can hand back, so the reference carries no `result_url`: an export is at somebody else's machine, which is where the caller asked for it.

func (*RestController) ExportItems

ExportItems answers POST /items:export: a query's result as a file, without a saved view. The query is read the way POST /items:query reads it (queryInput), so the grammar has one reader; the rendering is the view export's.

func (*RestController) ExportTenant

func (c *RestController) ExportTenant(
	w http.ResponseWriter, r *http.Request, tenantID openapi.AdminTenantId,
)

ExportTenant answers POST /admin/tenants/{tenantId}:export. Written out longhand for the same reason as its siblings above.

func (*RestController) ExportView

ExportView answers POST /views/{viewId}:export.

func (*RestController) ExtendDataSubjectRequest

func (c *RestController) ExtendDataSubjectRequest(
	w http.ResponseWriter, r *http.Request, requestID openapi_types.UUID,
	_ openapi.ExtendDataSubjectRequestParams,
)

ExtendDataSubjectRequest answers POST /privacy/requests/{requestId}:extend.

The body's three fields are required, and the generated type carries a missing one as its zero value. A field is passed on only when it is not that zero value, so a missing one reaches the registry as absent and is refused there with the code every other channel answers. A malformed day never gets this far: the decoder refuses it as a malformed body.

func (*RestController) FindDuplicatesByTitle

func (c *RestController) FindDuplicatesByTitle(w http.ResponseWriter, r *http.Request)

FindDuplicatesByTitle answers POST /items:duplicates.

A read sent as a POST for the reason `/search` is one: no idempotency key is read, and the answer is marked not to be stored by anything between the server and the person typing, because it is made of their words and their entries' titles.

func (*RestController) ForgetPassword

func (c *RestController) ForgetPassword(w http.ResponseWriter, r *http.Request)

ForgetPassword answers POST /auth/password:forgot.

Written out rather than through the identity helper: the route is public, because the whole point is that somebody who cannot sign in can reach it.

func (*RestController) ForgetSyncDevice

func (c *RestController) ForgetSyncDevice(w http.ResponseWriter, r *http.Request, deviceID openapi.DeviceId)

ForgetSyncDevice answers DELETE /sync/devices/{deviceId}.

func (*RestController) GetAccount

func (c *RestController) GetAccount(w http.ResponseWriter, r *http.Request, accountID openapi.AccountId)

GetAccount answers GET /accounts/{accountId}.

The read that turns the identifier in an `ActivityEntry.actor`, an assignee or a member row into a name. It answers `AccountSummary` rather than `Account`: a name and a kind, and none of the four fields that are the account holder's own business (`data-protection.md` §9).

func (*RestController) GetBackupRun

func (c *RestController) GetBackupRun(
	w http.ResponseWriter, r *http.Request, backupID openapi_types.UUID,
)

GetBackupRun answers GET /backups/{backupId}.

func (*RestController) GetCalendarFeedDocument

func (c *RestController) GetCalendarFeedDocument(
	w http.ResponseWriter, r *http.Request, presented string,
)

GetCalendarFeedDocument answers GET /calendar/{token}.ics - the one route in this API that carries no bearer credential.

What happens here is parsing, and nothing else. The shape of the token is a string question and belongs to an adapter; whether the feed exists, is revoked, still has a view and still has an owner who may read it are all decided inwards of here (ADR-0005). Every one of those answers looks the same from out here, which is the point: a route that distinguished them would answer questions for whoever is trying tokens (T-21).

func (*RestController) GetCapabilities

func (c *RestController) GetCapabilities(w http.ResponseWriter, r *http.Request)

GetCapabilities answers the manifest a client configures itself from (api-guidelines.md §1).

Public by the contract (`security: []`), and answered for an anonymous caller from the system-defined profiles alone. A caller that did present a credential sees its tenant's overrides where it has any - the same endpoint, a different answer, decided by the scope the use case opens rather than by a branch here.

func (*RestController) GetContainer

func (c *RestController) GetContainer(w http.ResponseWriter, r *http.Request, containerID openapi.ContainerId)

GetContainer answers GET /containers/{containerId}.

func (*RestController) GetGroup

func (c *RestController) GetGroup(w http.ResponseWriter, r *http.Request, groupID openapi.GroupId)

GetGroup answers GET /groups/{groupId}.

func (*RestController) GetHealthReport

func (c *RestController) GetHealthReport(w http.ResponseWriter, r *http.Request)

GetHealthReport answers GET /api/v1/meta/health.

It answers `200` even when the status is `down`, which the internal listener deliberately does not: the contract says in its own description that "the HTTP status describes whether the endpoint is reachable, the `status` field describes the system", and a client that receives a `503` here cannot tell a report that says "down" from a gateway that never reached this process. The operations listener keeps its `503` because a status page reads a status code and holds no token. The two differ on purpose.

Who sees how much is not decided here. The use case answers the whole report or the reduced one, because that is an authorisation decision and those live in the application layer (rule 2).

func (*RestController) GetImport

func (c *RestController) GetImport(w http.ResponseWriter, r *http.Request, importID openapi.ImportId)

GetImport answers GET /imports/{importId}.

func (*RestController) GetJob

func (c *RestController) GetJob(w http.ResponseWriter, r *http.Request, jobID openapi.JobId)

GetJob answers GET /jobs/{jobId}.

The resource a `202 Accepted` points at. Nothing here decides what a caller may see: the use case asks that question, and a job in another tenant reaches this method as the same not-found the catalogue answers for one that never existed.

func (*RestController) GetJumbleEntry

func (c *RestController) GetJumbleEntry(w http.ResponseWriter, r *http.Request, entryID openapi_types.UUID)

GetJumbleEntry answers GET /jumble/entries/{entryId}.

func (*RestController) GetMedia

func (c *RestController) GetMedia(w http.ResponseWriter, r *http.Request, mediaID openapi.MediaId)

GetMedia answers GET /media/{mediaId}.

The download target travels in the answer rather than as a redirect: a client that wants the record and a client that wants the bytes are asking two different questions, and a 302 here would force the first one to follow the second.

func (*RestController) GetOwnAccount

func (c *RestController) GetOwnAccount(w http.ResponseWriter, r *http.Request)

GetOwnAccount answers GET /accounts/me.

No parameter and no body: the actor is the identifier. `me` is a reserved segment rather than a possible account id - identifiers are UUIDs - so this route and /accounts/{accountId} cannot collide, and the specification says so where a reader will look.

Written out rather than through the `identity` helper below, the way every other handler that takes no path parameter is: the helper's closure hides the request from `contextcheck`, which then cannot see that the context travels.

func (*RestController) GetRecurrence

func (c *RestController) GetRecurrence(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId,
)

GetRecurrence answers GET /items/{itemId}/recurrence.

func (*RestController) GetRestoreRun

func (c *RestController) GetRestoreRun(
	w http.ResponseWriter, r *http.Request, restoreID openapi_types.UUID,
)

GetRestoreRun answers GET /restores/{restoreId}.

func (*RestController) GetRule

func (c *RestController) GetRule(
	w http.ResponseWriter, r *http.Request, ruleID openapi.RuleId,
)

GetAutomationRule answers GET /automation/rules/{ruleId}.

func (*RestController) GetRuleRun

func (c *RestController) GetRuleRun(
	w http.ResponseWriter, r *http.Request, runID openapi_types.UUID,
)

GetRuleRun answers GET /automation/runs/{runId}.

func (*RestController) GetSavedView

func (c *RestController) GetSavedView(w http.ResponseWriter, r *http.Request, viewID openapi.ViewId)

GetSavedView answers GET /views/{viewId}.

func (*RestController) GetSignInRules

func (c *RestController) GetSignInRules(w http.ResponseWriter, r *http.Request)

GetSignInRules answers GET /auth/sign-in-rules.

Written out rather than through the identity helper: the route is public, so there is no actor to resolve - the whole call exists so that somebody can produce one.

func (*RestController) GetTemplate

func (c *RestController) GetTemplate(
	w http.ResponseWriter, r *http.Request, templateID openapi.TemplateId,
)

GetTemplate answers GET /templates/{templateId}.

func (*RestController) GetWebhookSubscription

func (c *RestController) GetWebhookSubscription(
	w http.ResponseWriter, r *http.Request, webhookID openapi.WebhookId,
)

GetWebhookSubscription answers GET /integrations/webhooks/{webhookId}.

func (*RestController) GetWorkItem

func (c *RestController) GetWorkItem(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId, params openapi.GetWorkItemParams,
)

GetWorkItem answers GET /items/{itemId}.

func (*RestController) GiveConsent

func (c *RestController) GiveConsent(w http.ResponseWriter, r *http.Request)

GiveConsent answers POST /privacy/consents:give.

func (*RestController) GiveKudos

func (c *RestController) GiveKudos(w http.ResponseWriter, r *http.Request, itemID openapi.ItemId)

GiveKudos answers PUT /items/{itemId}/kudos.

func (*RestController) GrantMembership

func (c *RestController) GrantMembership(w http.ResponseWriter, r *http.Request, params openapi.GrantMembershipParams)

GrantMembership answers POST /memberships.

func (*RestController) HttpRequest

func (c *RestController) HttpRequest(w http.ResponseWriter, r *http.Request)

HttpRequest answers POST /integrations/http-requests.

func (*RestController) ImportEntries

ImportEntries answers POST /imports.

func (*RestController) InspectInvitation

func (c *RestController) InspectInvitation(w http.ResponseWriter, r *http.Request)

InspectInvitation answers POST /auth/invitations:inspect: the workspace and the address an invitation is for (UC-ID-07 check 1). Public, for the redemption's reason: the token in the body is the whole of what the call carries.

func (*RestController) InstantiateTemplate

func (c *RestController) InstantiateTemplate(
	w http.ResponseWriter, r *http.Request, templateID openapi.TemplateId,
	_ openapi.InstantiateTemplateParams,
)

InstantiateTemplate answers POST /templates/{templateId}:instantiate.

func (*RestController) InviteAccount

InviteAccount answers POST /accounts:invite.

func (*RestController) IssueStartPassword

func (c *RestController) IssueStartPassword(
	w http.ResponseWriter, r *http.Request, accountID openapi.AccountId, params openapi.IssueStartPasswordParams,
)

IssueStartPassword answers POST /accounts/{accountId}:issue-start-password.

The answer carries the new start password once, marked as the creation's is, so neither a cache nor an idempotency record keeps it.

func (*RestController) ListAccessTokens

func (c *RestController) ListAccessTokens(
	w http.ResponseWriter, r *http.Request, params openapi.ListAccessTokensParams,
)

ListAccessTokens answers GET /auth/tokens.

func (*RestController) ListActivity

func (c *RestController) ListActivity(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId, params openapi.ListActivityParams,
)

ListActivity answers GET /items/{itemId}/activity.

No ETag, for the reason no list has one: a page is not an entity with a version, and there is nothing here for a client to write back against - the history is append-only and nothing writes to it through this endpoint.

func (*RestController) ListAttachments

func (c *RestController) ListAttachments(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId,
	params openapi.ListAttachmentsParams,
)

ListAttachments answers GET /items/{itemId}/attachments.

func (*RestController) ListAuditEntries

func (c *RestController) ListAuditEntries(
	w http.ResponseWriter, r *http.Request, params openapi.ListAuditEntriesParams,
)

ListAuditEntries answers GET /audit.

The page is spelled `items` and `next_cursor` here rather than the `data`/`page` shape every other list uses, because that is what the contract declares for this path. The catalogue answers the ordinary shape and this is the one place it is renamed; renaming it in the contract instead would break whatever already reads it.

func (*RestController) ListBackupSchedules

func (c *RestController) ListBackupSchedules(w http.ResponseWriter, r *http.Request)

ListBackupSchedules answers GET /backup-schedules.

An array rather than a page, for `ListBackupTargets`' reason: a workspace has a handful of schedules, and a cursor over three rows would be ceremony.

func (*RestController) ListBackupTargets

func (c *RestController) ListBackupTargets(w http.ResponseWriter, r *http.Request)

ListBackupTargets answers GET /backup-targets.

An array rather than a page: a tenant has a handful of targets, and the 3-2-1 rule the document recommends tops out at three. A cursor over three rows would be ceremony.

func (*RestController) ListBackupsAtTarget

func (c *RestController) ListBackupsAtTarget(
	w http.ResponseWriter, r *http.Request,
	targetID openapi_types.UUID, params openapi.ListBackupsAtTargetParams,
)

ListBackupsAtTarget answers GET /backup-targets/{targetId}/backups.

An array rather than a page, for the reason the target list is one: a generation plan tops out at a few dozen archives, and a cursor over those would be ceremony. What the answer is made of comes from the manifests at the target - so this route is the one that still works on the day the database is a fresh empty one.

func (*RestController) ListBuckets

func (c *RestController) ListBuckets(
	w http.ResponseWriter, r *http.Request, containerID openapi.ContainerId,
)

ListBuckets answers GET /containers/{containerId}/buckets.

A plain array rather than a page, as the contract declares: a board has as many columns as fit on a screen (api-guidelines.md §2).

func (*RestController) ListCalendarFeeds

func (c *RestController) ListCalendarFeeds(w http.ResponseWriter, r *http.Request)

ListCalendarFeeds answers GET /integrations/calendar-feeds.

func (*RestController) ListComments

func (c *RestController) ListComments(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId,
	params openapi.ListCommentsParams,
)

ListComments answers GET /items/{itemId}/comments.

func (*RestController) ListConsents

func (c *RestController) ListConsents(w http.ResponseWriter, r *http.Request, params openapi.ListConsentsParams)

ListConsents answers GET /privacy/consents: the register, the one place who withdrew is listed.

func (*RestController) ListContainers

func (c *RestController) ListContainers(
	w http.ResponseWriter, r *http.Request, params openapi.ListContainersParams,
)

ListContainers answers GET /containers.

No ETag. A page is not an entity with a version - there is nothing for a client to write back against, and an entity tag over a list would have to change whenever any row in it did.

func (*RestController) ListCustomFields

func (c *RestController) ListCustomFields(
	w http.ResponseWriter, r *http.Request, params openapi.ListCustomFieldsParams,
)

ListCustomFields answers GET /custom-fields.

func (*RestController) ListDataSubjectRequests

func (c *RestController) ListDataSubjectRequests(
	w http.ResponseWriter, r *http.Request, params openapi.ListDataSubjectRequestsParams,
)

ListDataSubjectRequests answers GET /privacy/requests.

func (*RestController) ListEmergencyNotices

func (c *RestController) ListEmergencyNotices(w http.ResponseWriter, r *http.Request)

ListEmergencyNotices answers GET /accounts/me/emergency-notices.

func (*RestController) ListGroups

func (c *RestController) ListGroups(w http.ResponseWriter, r *http.Request, params openapi.ListGroupsParams)

ListGroups answers GET /groups.

func (*RestController) ListIdentityProviderPresets

func (c *RestController) ListIdentityProviderPresets(w http.ResponseWriter, r *http.Request)

ListIdentityProviderPresets answers GET /identity-provider-presets.

func (*RestController) ListIdentityProviders

func (c *RestController) ListIdentityProviders(w http.ResponseWriter, r *http.Request)

ListIdentityProviders answers GET /identity-providers.

func (*RestController) ListInstallationPresets

func (c *RestController) ListInstallationPresets(w http.ResponseWriter, r *http.Request)

ListInstallationPresets answers GET /admin/presets.

func (*RestController) ListInstanceAiProviders

func (c *RestController) ListInstanceAiProviders(w http.ResponseWriter, r *http.Request)

ListInstanceAiProviders answers GET /admin/ai-providers.

func (*RestController) ListInstanceBackupTargets

func (c *RestController) ListInstanceBackupTargets(w http.ResponseWriter, r *http.Request)

ListInstanceBackupTargets answers GET /admin/backup-targets.

func (*RestController) ListInstanceIdentityProviders

func (c *RestController) ListInstanceIdentityProviders(w http.ResponseWriter, r *http.Request)

ListInstanceIdentityProviders answers GET /admin/identity-providers.

func (*RestController) ListInstanceJournal

func (c *RestController) ListInstanceJournal(
	w http.ResponseWriter, r *http.Request, params openapi.ListInstanceJournalParams,
)

ListInstanceJournal answers GET /admin/journal.

func (*RestController) ListJumbleDestinations

func (c *RestController) ListJumbleDestinations(
	w http.ResponseWriter, r *http.Request, params openapi.ListJumbleDestinationsParams,
)

ListJumbleDestinations answers GET /jumble/destinations.

func (*RestController) ListJumbleEntries

func (c *RestController) ListJumbleEntries(
	w http.ResponseWriter, r *http.Request, params openapi.ListJumbleEntriesParams,
)

ListJumbleEntries answers GET /jumble/entries.

func (*RestController) ListLabels

func (c *RestController) ListLabels(
	w http.ResponseWriter, r *http.Request, containerID openapi.ContainerId,
	params openapi.ListLabelsParams,
)

ListLabels answers GET /containers/{containerId}/labels.

A plain array rather than a page, as the contract declares: a vocabulary people have to agree on is as long as a person can hold in their head (api-guidelines.md §2).

func (*RestController) ListLegalHolds

func (c *RestController) ListLegalHolds(
	w http.ResponseWriter, r *http.Request, params openapi.ListLegalHoldsParams,
)

ListLegalHolds answers GET /legal-holds.

An array rather than a page: a workspace has a handful of holds, and a workspace with enough of them to page through has a problem a cursor does not solve.

func (*RestController) ListMailReceipts

func (c *RestController) ListMailReceipts(w http.ResponseWriter, r *http.Request)

ListMailReceipts answers GET /mail/receipts.

func (*RestController) ListMailboxes

func (c *RestController) ListMailboxes(w http.ResponseWriter, r *http.Request)

ListMailboxes answers GET /mail/mailboxes.

func (*RestController) ListMemberships

func (c *RestController) ListMemberships(w http.ResponseWriter, r *http.Request, params openapi.ListMembershipsParams)

ListMemberships answers GET /memberships.

Through the shared read helper rather than the identity one: a list is mapped row by row from the catalogue's page shape, which is what `rowsOf` and `pageResponse` exist for.

func (*RestController) ListNotificationPreferences

func (c *RestController) ListNotificationPreferences(w http.ResponseWriter, r *http.Request, accountID openapi.AccountId)

ListNotificationPreferences answers GET /accounts/{accountId}/notification-preferences.

func (*RestController) ListOauthClients

func (c *RestController) ListOauthClients(w http.ResponseWriter, r *http.Request)

ListOauthClients answers GET /oauth/clients. Written out for ListServiceAccounts' reason.

func (*RestController) ListOauthGrants

func (c *RestController) ListOauthGrants(w http.ResponseWriter, r *http.Request)

ListOauthGrants answers GET /oauth/grants. Written out for ListServiceAccounts' reason.

func (*RestController) ListOperators

func (c *RestController) ListOperators(w http.ResponseWriter, r *http.Request)

ListOperators answers GET /admin/operators.

func (*RestController) ListOwnIdentities

func (c *RestController) ListOwnIdentities(w http.ResponseWriter, r *http.Request)

ListOwnIdentities answers GET /accounts/me/identities. Written out, GetOwnAccount's reasoning.

func (*RestController) ListOwnPasskeys

func (c *RestController) ListOwnPasskeys(w http.ResponseWriter, r *http.Request)

ListOwnPasskeys answers GET /accounts/me/passkeys. Written out, GetOwnAccount's reasoning.

func (*RestController) ListPlans

func (c *RestController) ListPlans(w http.ResponseWriter, r *http.Request)

ListPlans answers GET /admin/plans.

func (*RestController) ListPlatformDeliveries

func (c *RestController) ListPlatformDeliveries(
	w http.ResponseWriter, r *http.Request, subscriptionID openapi_types.UUID,
	params openapi.ListPlatformDeliveriesParams,
)

ListPlatformDeliveries answers GET /admin/subscriptions/{subscriptionId}/deliveries.

func (*RestController) ListPlatformEvents

func (c *RestController) ListPlatformEvents(
	w http.ResponseWriter, r *http.Request, params openapi.ListPlatformEventsParams,
)

ListPlatformEvents answers GET /admin/events: the installation's events, as the use case renders them, passed through unchanged.

func (*RestController) ListPlatformSubscriptions

func (c *RestController) ListPlatformSubscriptions(w http.ResponseWriter, r *http.Request)

ListPlatformSubscriptions answers GET /admin/subscriptions.

func (*RestController) ListPresets

func (c *RestController) ListPresets(w http.ResponseWriter, r *http.Request)

ListPresets answers GET /presets.

func (*RestController) ListPrivateHubAccesses

func (c *RestController) ListPrivateHubAccesses(w http.ResponseWriter, r *http.Request, hubID openapi.HubId)

ListPrivateHubAccesses answers GET /private-hubs/{hubId}/accesses.

func (*RestController) ListPrivateHubs

func (c *RestController) ListPrivateHubs(w http.ResponseWriter, r *http.Request)

ListPrivateHubs answers GET /private-hubs.

func (*RestController) ListReminders

func (c *RestController) ListReminders(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId,
)

ListReminders answers GET /items/{itemId}/reminders.

func (*RestController) ListRetentionPolicies

func (c *RestController) ListRetentionPolicies(
	w http.ResponseWriter, r *http.Request, params openapi.ListRetentionPoliciesParams,
)

ListRetentionPolicies answers GET /retention-policies.

An array rather than a page: a workspace has a rule per kind per level, and the catalogue has fifteen kinds. A cursor over that would be ceremony.

func (*RestController) ListRuleRuns

func (c *RestController) ListRuleRuns(
	w http.ResponseWriter, r *http.Request, params openapi.ListRuleRunsParams,
)

ListRuleRuns answers GET /automation/runs.

func (*RestController) ListRules

func (c *RestController) ListRules(
	w http.ResponseWriter, r *http.Request, params openapi.ListRulesParams,
)

ListAutomationRules answers GET /automation/rules.

func (*RestController) ListSavedViews

func (c *RestController) ListSavedViews(
	w http.ResponseWriter, r *http.Request, params openapi.ListSavedViewsParams,
)

ListSavedViews answers GET /views.

func (*RestController) ListServiceAccounts

func (c *RestController) ListServiceAccounts(w http.ResponseWriter, r *http.Request)

ListServiceAccounts answers GET /auth/service-accounts.

Written out rather than through the identity helper, for the reason ListCalendarFeeds is: the helper's closure takes no context, and an operation with no parameters gives the linter nothing to trace the request's context through.

func (*RestController) ListSessions

func (c *RestController) ListSessions(w http.ResponseWriter, r *http.Request)

ListSessions answers GET /auth/sessions.

Written out rather than through the identity helper, for the reason ListServiceAccounts is: the operation has no parameters, and the helper's closure gives the linter nothing to trace the request's context through.

func (*RestController) ListSuggestions

func (c *RestController) ListSuggestions(
	w http.ResponseWriter, r *http.Request, params openapi.ListSuggestionsParams,
)

ListSuggestions answers GET /suggestions.

func (*RestController) ListSyncDevices

func (c *RestController) ListSyncDevices(w http.ResponseWriter, r *http.Request)

ListSyncDevices answers GET /sync/devices. Written out rather than through the identity helper, for the reason ListSessions is.

func (*RestController) ListTemplates

func (c *RestController) ListTemplates(
	w http.ResponseWriter, r *http.Request, params openapi.ListTemplatesParams,
)

ListTemplates answers GET /templates.

func (*RestController) ListTenants

func (c *RestController) ListTenants(w http.ResponseWriter, r *http.Request)

ListTenants answers GET /admin/tenants.

func (*RestController) ListTrash

func (c *RestController) ListTrash(
	w http.ResponseWriter, r *http.Request, params openapi.ListTrashParams,
)

ListTrash answers GET /trash.

The page can be shorter than the size asked for, and that is the contract rather than a bug: the trash spans hubs, so it is narrowed to what the caller may see rather than refused, and the cursor is a boundary in what was scanned rather than in what came back. A client pages on until has_more is false (api-guidelines.md §4).

func (*RestController) ListWebhookDeliveries

func (c *RestController) ListWebhookDeliveries(
	w http.ResponseWriter, r *http.Request, webhookID openapi.WebhookId,
	params openapi.ListWebhookDeliveriesParams,
)

ListWebhookDeliveries answers GET /integrations/webhooks/{webhookId}/deliveries.

func (*RestController) ListWebhookSubscriptions

func (c *RestController) ListWebhookSubscriptions(w http.ResponseWriter, r *http.Request)

ListWebhookSubscriptions answers GET /integrations/webhooks.

Written out rather than through the identity helper, for ListServiceAccounts' reason: the helper's closure takes no context, and an operation with no parameters gives the linter nothing to trace the request's context through.

func (*RestController) ListWorkItems

func (c *RestController) ListWorkItems(
	w http.ResponseWriter, r *http.Request, params openapi.ListWorkItemsParams,
)

ListWorkItems answers GET /items.

func (*RestController) LocateOidcFlow

func (c *RestController) LocateOidcFlow(w http.ResponseWriter, r *http.Request)

LocateOidcFlow answers POST /auth/oidc:locate: where a provider's return belongs. Anonymous, as the return is, and in the auth bucket (RateLimit.go).

func (*RestController) MakeEntries

func (c *RestController) MakeEntries(
	w http.ResponseWriter, r *http.Request, containerID openapi.ContainerId, _ openapi.MakeEntriesParams,
)

MakeEntries answers POST /containers/{containerId}:make-entries: 202 with the job that turns the text into a proposal of entries.

func (*RestController) MoveContainer

func (c *RestController) MoveContainer(
	w http.ResponseWriter, r *http.Request,
	containerID openapi.ContainerId, _ openapi.MoveContainerParams,
)

MoveContainer answers POST /containers/{containerId}:move.

The body is required and `target_parent_id` with it: unlike an item, a collection has no level above its hub to be moved to, so there is no null that would mean anything here.

func (*RestController) MoveTenantPlan

MoveTenantPlan answers POST /admin/tenants/{tenantId}:move-plan. A POST so that the replay guard keeps its answer under an idempotency key, as it does for every control-plane act.

func (*RestController) MoveWorkItem

func (c *RestController) MoveWorkItem(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId, _ openapi.MoveWorkItemParams,
)

MoveWorkItem answers POST /items/{itemId}:move.

func (*RestController) OfferIdentityProvider

func (c *RestController) OfferIdentityProvider(
	w http.ResponseWriter, r *http.Request, providerID openapi.ProviderId,
	params openapi.OfferIdentityProviderParams,
)

OfferIdentityProvider answers POST /identity-providers/{providerId}:offer.

func (*RestController) OpenPrivateHub

func (c *RestController) OpenPrivateHub(
	w http.ResponseWriter, r *http.Request, hubID openapi.HubId, params openapi.OpenPrivateHubParams,
)

OpenPrivateHub answers POST /private-hubs/{hubId}/accesses: the workspace owner's emergency access.

func (*RestController) OpenTenantPassword

func (c *RestController) OpenTenantPassword(
	w http.ResponseWriter, r *http.Request,
	tenantID openapi.AdminTenantId, params openapi.OpenTenantPasswordParams,
)

OpenTenantPassword answers POST /admin/tenants/{tenantId}:open-password. Written out longhand for the same reason as its siblings above.

func (*RestController) PlaceLegalHold

func (c *RestController) PlaceLegalHold(w http.ResponseWriter, r *http.Request)

PlaceLegalHold answers POST /legal-holds.

func (*RestController) PollTriggerEvents

func (c *RestController) PollTriggerEvents(
	w http.ResponseWriter, r *http.Request, eventType openapi.EventType,
	params openapi.PollTriggerEventsParams,
)

PollTriggerEvents answers GET /integrations/triggers/{eventType}.

The entries pass through unread. What the use case rendered is the CloudEvents document a webhook delivery would have carried, and a controller that re-assembled it field by field would be a second statement of the wire format - one that drops an extension attribute the day one is added, silently and only for the pull half.

func (*RestController) PreviewContainerStructure

func (c *RestController) PreviewContainerStructure(
	w http.ResponseWriter, r *http.Request, containerID openapi.ContainerId,
)

PreviewContainerStructure answers POST /containers/{containerId}/structure:preview: what the change in the body would stop showing, decided as PATCH .../structure would decide it.

func (*RestController) PreviewErasure

func (c *RestController) PreviewErasure(
	w http.ResponseWriter, r *http.Request, requestID openapi_types.UUID,
	params openapi.PreviewErasureParams,
)

PreviewErasure answers GET /privacy/requests/{requestId}/erasure-preview.

func (*RestController) PreviewRetentionPolicy

func (c *RestController) PreviewRetentionPolicy(
	w http.ResponseWriter, r *http.Request, policyID openapi_types.UUID,
)

PreviewRetentionPolicy answers POST /retention-policies/{policyId}:preview.

func (*RestController) ProvisionTenant

ProvisionTenant answers POST /admin/tenants.

func (*RestController) PurgeWorkItem

func (c *RestController) PurgeWorkItem(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId, _ openapi.PurgeWorkItemParams,
)

PurgeWorkItem answers POST /items/{itemId}:purge.

204 rather than a count. The caller named one entry, and how many rows hung off it is not a fact about their request: what they asked was "make this go", and the answer is that it has (api-guidelines.md §2). The count is in the audit trail, where the question is asked afterwards.

func (*RestController) QueryItems

func (c *RestController) QueryItems(w http.ResponseWriter, r *http.Request)

QueryItems answers POST /items:query.

A POST that reads: the query is a document rather than a set of parameters, and a URL long enough to carry a filter tree is a URL a proxy truncates. Nothing is written, and the same request may be repeated (api-guidelines.md §3).

func (*RestController) QueryTenants

func (c *RestController) QueryTenants(w http.ResponseWriter, r *http.Request)

QueryTenants answers POST /admin/tenants:query.

func (*RestController) ReadAiBudgetState

func (c *RestController) ReadAiBudgetState(w http.ResponseWriter, r *http.Request)

ReadAiBudgetState answers GET /ai-budget-state: how today's AI budget stands, for whoever is about to ask (UC-AI-04 check 9).

func (*RestController) ReadAiProvider

func (c *RestController) ReadAiProvider(w http.ResponseWriter, r *http.Request)

ReadAiProvider answers GET /ai-provider.

func (*RestController) ReadAiSource

func (c *RestController) ReadAiSource(w http.ResponseWriter, r *http.Request)

ReadAiSource answers GET /ai-source.

func (*RestController) ReadEncryptionStatus

func (c *RestController) ReadEncryptionStatus(w http.ResponseWriter, r *http.Request)

ReadEncryptionStatus answers GET /admin/encryption: the keyring and the census over it (ADR-0045, security.md §8.1).

func (*RestController) ReadIdentityProvider

func (c *RestController) ReadIdentityProvider(w http.ResponseWriter, r *http.Request)

ReadIdentityProvider answers GET /identity-provider — the singular surface, kept.

func (*RestController) ReadInstanceOverview

func (c *RestController) ReadInstanceOverview(w http.ResponseWriter, r *http.Request)

ReadInstanceOverview answers GET /admin/overview.

func (*RestController) ReadInstanceSettings

func (c *RestController) ReadInstanceSettings(w http.ResponseWriter, r *http.Request)

ReadInstanceSettings answers GET /admin/settings.

func (*RestController) ReadKudos

func (c *RestController) ReadKudos(w http.ResponseWriter, r *http.Request, itemID openapi.ItemId)

ReadKudos answers GET /items/{itemId}/kudos.

func (*RestController) ReadOauthClient

func (c *RestController) ReadOauthClient(
	w http.ResponseWriter, r *http.Request, clientID openapi.OauthClientId,
)

ReadOauthClient answers GET /oauth/clients/{clientId}: what the app is called, for the person being asked to allow it.

func (*RestController) ReadOwnAiPosition

func (c *RestController) ReadOwnAiPosition(w http.ResponseWriter, r *http.Request)

ReadOwnAiPosition answers GET /privacy/ai-objection.

func (*RestController) ReadPlan

func (c *RestController) ReadPlan(w http.ResponseWriter, r *http.Request, planName string)

ReadPlan answers GET /admin/plans/{planName}.

func (*RestController) ReadPlatformSubscription

func (c *RestController) ReadPlatformSubscription(
	w http.ResponseWriter, r *http.Request, subscriptionID openapi_types.UUID,
)

ReadPlatformSubscription answers GET /admin/subscriptions/{subscriptionId}.

func (*RestController) ReadQuotas

func (c *RestController) ReadQuotas(w http.ResponseWriter, r *http.Request)

ReadQuotas answers GET /quotas. Written out longhand for ListServiceAccounts' reason.

func (*RestController) ReadTenant

func (c *RestController) ReadTenant(w http.ResponseWriter, r *http.Request, tenantID openapi.AdminTenantId)

ReadTenant answers GET /admin/tenants/{tenantId}, with the version as the tag.

func (*RestController) ReadUsage

ReadUsage answers GET /admin/usage: the use per workspace, metric and day, as the use case lays it out.

func (*RestController) ReadWorkspace

func (c *RestController) ReadWorkspace(w http.ResponseWriter, r *http.Request)

ReadWorkspace answers GET /tenant.

Written out rather than through the identity helper, for `ListWebhookSubscriptions`' reason: the helper's closure takes no context, and an operation with no parameters gives the linter nothing to trace the request's context through.

func (*RestController) ReapplyErasures

func (c *RestController) ReapplyErasures(
	w http.ResponseWriter, r *http.Request, tenantID openapi.AdminTenantId, params openapi.ReapplyErasuresParams,
)

ReapplyErasures answers POST /admin/tenants/{tenantId}:reapply-erasures. The cases travel to the use case as JSON objects of strings, with their instants written out again: the use case reads the same shape over every channel.

func (*RestController) RedeemInvitation

func (c *RestController) RedeemInvitation(w http.ResponseWriter, r *http.Request)

RedeemInvitation answers POST /auth/invitations:redeem.

Written out for SignIn's reason: the route is public, and the token in the body is the whole of what authenticates the call.

func (*RestController) RedeliverPlatformSubscription

func (c *RestController) RedeliverPlatformSubscription(
	w http.ResponseWriter, r *http.Request, subscriptionID openapi_types.UUID,
)

RedeliverPlatformSubscription answers POST /admin/subscriptions/{subscriptionId}:redeliver.

func (*RestController) RefreshSession

func (c *RestController) RefreshSession(w http.ResponseWriter, r *http.Request)

RefreshSession answers POST /auth/sessions:refresh.

func (*RestController) RegenerateRecoveryCodes

func (c *RestController) RegenerateRecoveryCodes(
	w http.ResponseWriter, r *http.Request, params openapi.RegenerateRecoveryCodesParams,
)

RegenerateRecoveryCodes answers POST /auth/mfa/recovery:regenerate.

func (*RestController) RegisterOauthClient

func (c *RestController) RegisterOauthClient(
	w http.ResponseWriter, r *http.Request, _ openapi.RegisterOauthClientParams,
)

RegisterOauthClient answers POST /oauth/clients.

func (*RestController) ReindexSearch

func (c *RestController) ReindexSearch(w http.ResponseWriter, r *http.Request)

ReindexSearch answers POST /search:reindex: the job to watch, and how many rows it will rewrite.

func (*RestController) ReleaseLegalHold

func (c *RestController) ReleaseLegalHold(
	w http.ResponseWriter, r *http.Request, holdID openapi_types.UUID,
)

ReleaseLegalHold answers POST /legal-holds/{holdId}:release.

func (*RestController) RemoveAiProvider

func (c *RestController) RemoveAiProvider(w http.ResponseWriter, r *http.Request)

RemoveAiProvider answers DELETE /ai-provider.

func (*RestController) RemoveIdentityProvider

func (c *RestController) RemoveIdentityProvider(
	w http.ResponseWriter, r *http.Request, providerID openapi.ProviderId,
	params openapi.RemoveIdentityProviderParams,
)

RemoveIdentityProvider answers DELETE /identity-providers/{providerId}.

func (*RestController) RemoveInstanceAiProvider

func (c *RestController) RemoveInstanceAiProvider(
	w http.ResponseWriter, r *http.Request, providerID openapi.ProviderId,
)

RemoveInstanceAiProvider answers DELETE /admin/ai-providers/{providerId}.

func (*RestController) RemoveInstanceIdentityProvider

func (c *RestController) RemoveInstanceIdentityProvider(
	w http.ResponseWriter, r *http.Request, providerID openapi.ProviderId,
	params openapi.RemoveInstanceIdentityProviderParams,
)

RemoveInstanceIdentityProvider answers DELETE /admin/identity-providers/{providerId}.

func (*RestController) RemoveLabel

func (c *RestController) RemoveLabel(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId, labelID openapi.LabelId,
)

RemoveLabel answers DELETE /items/{itemId}/labels/{labelId}.

func (*RestController) RemoveMailbox

func (c *RestController) RemoveMailbox(w http.ResponseWriter, r *http.Request, mailboxID openapi_types.UUID)

RemoveMailbox answers DELETE /mail/mailboxes/{mailbox_id}.

func (*RestController) RemoveMember

func (c *RestController) RemoveMember(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId, accountID openapi.AccountId,
)

RemoveMember answers DELETE /items/{itemId}/members/{accountId}.

func (*RestController) RemoveOperator

func (c *RestController) RemoveOperator(
	w http.ResponseWriter, r *http.Request, accountID openapi.AccountId,
)

RemoveOperator answers DELETE /admin/operators/{accountId}.

func (*RestController) RemoveOwnAvatar

func (c *RestController) RemoveOwnAvatar(w http.ResponseWriter, r *http.Request)

RemoveOwnAvatar answers DELETE /accounts/me/avatar.

func (*RestController) RemoveOwnPasskey

func (c *RestController) RemoveOwnPasskey(
	w http.ResponseWriter, r *http.Request, passkeyID openapi.PasskeyId, params openapi.RemoveOwnPasskeyParams,
)

RemoveOwnPasskey answers DELETE /accounts/me/passkeys/{passkeyId}.

func (*RestController) RemovePlan

func (c *RestController) RemovePlan(w http.ResponseWriter, r *http.Request, planName string)

RemovePlan answers DELETE /admin/plans/{planName}.

func (*RestController) RemoveRecurrence

func (c *RestController) RemoveRecurrence(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId,
	params openapi.RemoveRecurrenceParams,
)

RemoveRecurrence answers DELETE /items/{itemId}/recurrence. 204: the caller asked for the series to be gone, and the occurrences it produced are read through the entries they are.

func (*RestController) RenameContainer

func (c *RestController) RenameContainer(
	w http.ResponseWriter, r *http.Request,
	containerID openapi.ContainerId, params openapi.RenameContainerParams,
)

RenameContainer answers PATCH /containers/{containerId}.

A JSON Merge Patch (RFC 7386), which is why presence is read rather than the value alone: an absent `icon` means "leave it alone" and `"icon": null` means "clear it", and both arrive as a nil pointer in the generated struct. A handler that could not tell them apart would clear the icon of every client that only meant to rename something.

Null reaches the catalogue as the empty string. The domain holds these fields as strings whose empty value is "not set", so cleared and empty are one state there - and inventing a second spelling for it in this layer would be a distinction only this layer believed in.

func (*RestController) RenameOwnPasskey

func (c *RestController) RenameOwnPasskey(w http.ResponseWriter, r *http.Request, passkeyID openapi.PasskeyId)

RenameOwnPasskey answers PATCH /accounts/me/passkeys/{passkeyId}.

func (*RestController) RenewInvitation

func (c *RestController) RenewInvitation(
	w http.ResponseWriter, r *http.Request, accountID openapi.AccountId, _ openapi.RenewInvitationParams,
)

RenewInvitation answers POST /accounts/{accountId}:renew-invitation, with the invitation's answer and its marks.

func (*RestController) ReopenWorkItem

func (c *RestController) ReopenWorkItem(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId, _ openapi.ReopenWorkItemParams,
)

ReopenWorkItem answers POST /items/{itemId}:reopen. No body: there is nothing to say beyond which item.

func (*RestController) ReorderBucket

ReorderBucket answers POST /containers/{containerId}/buckets/{bucketId}:reorder.

func (*RestController) ReorderContainer

func (c *RestController) ReorderContainer(
	w http.ResponseWriter, r *http.Request,
	containerID openapi.ContainerId, _ openapi.ReorderContainerParams,
)

ReorderContainer answers POST /containers/{containerId}:reorder.

The body is optional and so is its one field: a reorder with nothing in it appends to the end of the level, which is a position like any other. That is the difference from `:move`, where the destination is required because there is no default hub to mean.

func (*RestController) ReorderWorkItem

func (c *RestController) ReorderWorkItem(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId, _ openapi.ReorderWorkItemParams,
)

ReorderWorkItem answers POST /items/{itemId}:reorder.

func (*RestController) ReplaceLegalHolds

func (c *RestController) ReplaceLegalHolds(
	w http.ResponseWriter, r *http.Request, tenantID openapi.AdminTenantId,
)

ReplaceLegalHolds answers POST /admin/tenants/{tenantId}:replace-legal-holds. The records travel to the use case as the JSON objects they arrived as, with their instants written out again: the use case reads the same shape over every channel.

func (*RestController) ReplayRuleRun

ReplayRuleRun answers POST /automation/runs/{runId}:replay.

func (*RestController) ReplayWebhookDelivery

func (c *RestController) ReplayWebhookDelivery(
	w http.ResponseWriter, r *http.Request, webhookID openapi.WebhookId, deliveryID openapi.DeliveryId,
)

ReplayWebhookDelivery answers POST /integrations/webhooks/{id}/deliveries/{deliveryId}:replay.

func (*RestController) RequestAddress

func (c *RestController) RequestAddress(w http.ResponseWriter, r *http.Request, accountID openapi.AccountId)

RequestAddress answers POST /accounts/{accountId}:add-address.

func (*RestController) RequestMediaUpload

func (c *RestController) RequestMediaUpload(
	w http.ResponseWriter, r *http.Request, _ openapi.RequestMediaUploadParams,
)

RequestMediaUpload answers POST /media.

The answer carries the upload target, which is the point of the whole operation: on an object-storage installation it is a presigned bucket URL and the bytes never touch this server (arc42 §8.4); on a local one it is this server's own content route, with a token standing in for the signature.

func (*RestController) RequestTenantDeletion

func (c *RestController) RequestTenantDeletion(
	w http.ResponseWriter, r *http.Request,
	tenantID openapi.AdminTenantId, params openapi.RequestTenantDeletionParams,
)

RequestTenantDeletion answers POST /admin/tenants/{tenantId}:delete. Written out longhand for the same reason as its siblings above.

func (*RestController) ResealSecrets

func (c *RestController) ResealSecrets(w http.ResponseWriter, r *http.Request)

ResealSecrets answers POST /admin/encryption:reseal with 202: the rounds are queued, and the status route is where their progress is read.

func (*RestController) ResetPassword

func (c *RestController) ResetPassword(w http.ResponseWriter, r *http.Request)

ResetPassword answers POST /auth/password:reset.

func (*RestController) RestoreContainer

func (c *RestController) RestoreContainer(
	w http.ResponseWriter, r *http.Request,
	containerID openapi.ContainerId, _ openapi.RestoreContainerParams,
)

RestoreContainer answers POST /containers/{containerId}:restore.

func (*RestController) RestoreLabel

RestoreLabel answers POST /containers/{containerId}/labels/{labelId}:restore.

No If-Match: the precondition of a restore is the deletion, not a version, and a retry after a lost answer has to succeed rather than meet the version its own first attempt moved on. The Idempotency-Key is the transport's (api-guidelines.md §5).

func (*RestController) RestoreWorkItem

func (c *RestController) RestoreWorkItem(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId, _ openapi.RestoreWorkItemParams,
)

RestoreWorkItem answers POST /items/{itemId}:restore.

func (*RestController) RestrictProcessing

func (c *RestController) RestrictProcessing(
	w http.ResponseWriter, r *http.Request, accountID openapi.AccountId,
)

RestrictProcessing answers POST /accounts/{accountId}:restrict.

func (*RestController) ResumeTenant

func (c *RestController) ResumeTenant(
	w http.ResponseWriter, r *http.Request, tenantID openapi.AdminTenantId,
)

ResumeTenant answers POST /admin/tenants/{tenantId}:resume.

func (*RestController) RetainItem

func (c *RestController) RetainItem(
	w http.ResponseWriter, r *http.Request, itemID openapi_types.UUID,
)

RetainItem answers POST /items/{itemId}:retain.

func (*RestController) RevokeAccessToken

func (c *RestController) RevokeAccessToken(w http.ResponseWriter, r *http.Request, tokenID openapi.TokenId)

RevokeAccessToken answers DELETE /auth/tokens/{tokenId}.

func (*RestController) RevokeAllSessions

func (c *RestController) RevokeAllSessions(w http.ResponseWriter, r *http.Request)

RevokeAllSessions answers DELETE /auth/sessions.

func (*RestController) RevokeCalendarFeed

func (c *RestController) RevokeCalendarFeed(
	w http.ResponseWriter, r *http.Request, feedID openapi.FeedId,
)

RevokeCalendarFeed answers DELETE /integrations/calendar-feeds/{feedId}.

func (*RestController) RevokeMembership

func (c *RestController) RevokeMembership(w http.ResponseWriter, r *http.Request, membershipID openapi.MembershipId, params openapi.RevokeMembershipParams)

RevokeMembership answers DELETE /memberships/{membershipId}.

func (*RestController) RevokeOauthGrant

func (c *RestController) RevokeOauthGrant(
	w http.ResponseWriter, r *http.Request, grantID openapi.OauthGrantId,
)

RevokeOauthGrant answers DELETE /oauth/grants/{grantId}.

func (*RestController) RevokeOtherSessions

func (c *RestController) RevokeOtherSessions(w http.ResponseWriter, r *http.Request)

RevokeOtherSessions answers POST /auth/sessions:revoke-others.

func (*RestController) RevokeSession

func (c *RestController) RevokeSession(w http.ResponseWriter, r *http.Request, sessionID openapi.SessionId)

RevokeSession answers DELETE /auth/sessions/{sessionId}.

func (*RestController) RotateInboundTrigger

func (c *RestController) RotateInboundTrigger(
	w http.ResponseWriter, r *http.Request, ruleID openapi.RuleId,
	_ openapi.RotateInboundTriggerParams,
)

RotateInboundTrigger answers POST /automation/rules/{ruleId}:rotate-inbound-token.

The one answer that carries the token. Everything afterwards shows only when it was minted: the value is stored hashed, and there is no read that can produce it again.

func (*RestController) RotateJumbleIntake

func (c *RestController) RotateJumbleIntake(
	w http.ResponseWriter, r *http.Request, _ openapi.RotateJumbleIntakeParams,
)

RotateJumbleIntake answers POST /jumble/intake:rotate-token.

func (*RestController) RotatePlatformSubscriptionSecret

func (c *RestController) RotatePlatformSubscriptionSecret(
	w http.ResponseWriter, r *http.Request, subscriptionID openapi_types.UUID,
	params openapi.RotatePlatformSubscriptionSecretParams,
)

RotatePlatformSubscriptionSecret answers POST /admin/subscriptions/{subscriptionId}:rotate-secret.

func (*RestController) RotateWebhookSecret

func (c *RestController) RotateWebhookSecret(
	w http.ResponseWriter, r *http.Request, webhookID openapi.WebhookId,
	_ openapi.RotateWebhookSecretParams,
)

RotateWebhookSecret answers POST /integrations/webhooks/{webhookId}:rotate-secret.

func (*RestController) Routes

func (c *RestController) Routes() *Mux

Routes builds the router from the generated registration list. Nothing here names a path: the paths come from the specification through the generated code, which is what keeps the router from drifting away from the contract (ADR-0004).

func (*RestController) SaveMailSettings

SaveMailSettings answers PUT /mail/settings.

func (*RestController) SavePreset

func (c *RestController) SavePreset(w http.ResponseWriter, r *http.Request)

throughJSON maps a catalogue output onto a generated schema by its JSON spelling. Used for the structure documents, whose output is already in the contract's words: a field-by-field copy would be a second spelling of eight settings to keep in step. SavePreset answers POST /presets.

func (*RestController) SaveVoiceSettings

SaveVoiceSettings answers PUT /voice/settings.

func (*RestController) SearchItems

func (c *RestController) SearchItems(w http.ResponseWriter, r *http.Request)

SearchItems answers POST /search.

A POST that reads, exactly as the query is: the request is a document, and a URL long enough to carry a search phrase and a scope is a URL a proxy truncates. Nothing is written and the same request may be repeated (api-guidelines.md §3).

func (*RestController) SendWebhook

func (c *RestController) SendWebhook(
	w http.ResponseWriter, r *http.Request, webhookID openapi.WebhookId,
)

SendWebhook answers POST /integrations/webhooks/{webhookId}:send.

func (*RestController) SetAiArrivalProposals

func (c *RestController) SetAiArrivalProposals(w http.ResponseWriter, r *http.Request)

SetAiArrivalProposals answers PUT /ai-arrival-proposals.

func (*RestController) SetAiPersonOptOut

func (c *RestController) SetAiPersonOptOut(w http.ResponseWriter, r *http.Request)

SetAiPersonOptOut answers PUT /ai-person-opt-out.

func (*RestController) SetCollectionHandle

func (c *RestController) SetCollectionHandle(
	w http.ResponseWriter, r *http.Request, collectionID openapi_types.UUID, _ openapi.SetCollectionHandleParams,
)

SetCollectionHandle answers PUT /collections/{collection_id}/handle.

func (*RestController) SetContainerStructure

func (c *RestController) SetContainerStructure(
	w http.ResponseWriter, r *http.Request,
	containerID openapi.ContainerId, params openapi.SetContainerStructureParams,
)

SetContainerStructure answers PATCH /containers/{containerId}/structure.

The body is decoded into the generated type first, so an unknown key is refused here as on every other route; the settings then travel to the catalogue in the contract's words, the same document an MCP client sends.

func (*RestController) SetCover

func (c *RestController) SetCover(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId, params openapi.SetCoverParams,
)

SetCover answers PUT /items/{itemId}/cover.

func (*RestController) SetCustomField

SetCustomField answers PUT /items/{itemId}/custom-fields/{key}.

One key per call, which is the merge rule showing through the URL: the values merge per key, so the resource a client addresses is one key rather than the document (offline-sync.md §4.2).

func (*RestController) SetDueDate

func (c *RestController) SetDueDate(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId, params openapi.SetDueDateParams,
)

SetDueDate answers PUT /items/{itemId}/due.

func (*RestController) SetHubPrivacy

func (c *RestController) SetHubPrivacy(
	w http.ResponseWriter, r *http.Request,
	containerID openapi.ContainerId, params openapi.SetHubPrivacyParams,
)

SetHubPrivacy answers PUT /containers/{containerId}/privacy.

func (*RestController) SetNotificationPreference

func (c *RestController) SetNotificationPreference(
	w http.ResponseWriter, r *http.Request, accountID openapi.AccountId, category string, channel string,
)

SetNotificationPreference answers PUT /accounts/{accountId}/notification-preferences/{category}/{channel}.

func (*RestController) SetOwnAiBudget

func (c *RestController) SetOwnAiBudget(w http.ResponseWriter, r *http.Request)

SetOwnAiBudget answers PUT /ai-budget: the workspace's own budget for its own model, and the two AI standings after it. A null leaves the field out of the input, which is "no budget".

func (*RestController) SetOwnAvatar

func (c *RestController) SetOwnAvatar(w http.ResponseWriter, r *http.Request)

SetOwnAvatar answers PUT /accounts/me/avatar: the caller's own picture. Written out like GetOwnAccount, for the reason given there.

func (*RestController) SetPasswordAndSignIn

func (c *RestController) SetPasswordAndSignIn(w http.ResponseWriter, r *http.Request)

SetPasswordAndSignIn answers POST /auth/sessions:set-password.

Written out rather than through the identity helper: the route is public for the pending credential's reason - the credential in the body is the whole of what authenticates the call.

func (*RestController) SetRecurrence

func (c *RestController) SetRecurrence(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId,
	params openapi.SetRecurrenceParams,
)

SetRecurrence answers PUT /items/{itemId}/recurrence.

func (*RestController) SetUpInstallation

func (c *RestController) SetUpInstallation(w http.ResponseWriter, r *http.Request)

SetUpInstallation answers POST /auth/installation:set-up.

Written out for SignIn's reason: the route is public, and the setup code in the body is the whole of what authenticates the call.

func (*RestController) SetWorkspaceStructure

func (c *RestController) SetWorkspaceStructure(
	w http.ResponseWriter, r *http.Request, params openapi.SetWorkspaceStructureParams,
)

SetWorkspaceStructure answers PATCH /tenant/structure.

`default_preset: null` removes the default, and an absent key leaves it: the two are told apart by presence, which the generated type cannot do.

func (*RestController) ShareSavedView

func (c *RestController) ShareSavedView(
	w http.ResponseWriter, r *http.Request, viewID openapi.ViewId, params openapi.ShareSavedViewParams,
)

ShareSavedView answers POST /views/{viewId}:share.

func (*RestController) ShowAlexaModel

func (c *RestController) ShowAlexaModel(w http.ResponseWriter, r *http.Request, params openapi.ShowAlexaModelParams)

ShowAlexaModel answers GET /voice/alexa/model.

func (*RestController) ShowMailSettings

func (c *RestController) ShowMailSettings(w http.ResponseWriter, r *http.Request)

ShowMailSettings answers GET /mail/settings.

func (*RestController) ShowVoiceSettings

func (c *RestController) ShowVoiceSettings(w http.ResponseWriter, r *http.Request)

ShowVoiceSettings answers GET /voice/settings. Written out rather than through the identity helper, for ListServiceAccounts' reason: with no parameters, the helper's closure gives the linter nothing to trace the request's context through.

func (*RestController) SignIn

func (c *RestController) SignIn(w http.ResponseWriter, r *http.Request)

SignIn answers POST /auth/sessions.

Written out rather than through the identity helper: the route is public, so there is no actor to resolve - the whole call exists to produce one.

func (*RestController) SkipOccurrence

func (c *RestController) SkipOccurrence(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId,
	_ openapi.SkipOccurrenceParams,
)

SkipOccurrence answers POST /items/{itemId}/recurrence:skip.

func (*RestController) StartAuthenticatorReplacement

func (c *RestController) StartAuthenticatorReplacement(
	w http.ResponseWriter, r *http.Request, params openapi.StartAuthenticatorReplacementParams,
)

StartAuthenticatorReplacement answers POST /auth/mfa/totp:replace.

func (*RestController) StartBackup

func (c *RestController) StartBackup(w http.ResponseWriter, r *http.Request)

StartBackup answers POST /backups.

202 with a JobRef, because the run takes minutes: the archive is written by a worker, and what the caller gets now is something to poll. `result_url` names the run, whose identifier is also the archive's identifier in the manifest at the target.

func (*RestController) StartInboundRun

func (c *RestController) StartInboundRun(
	w http.ResponseWriter, r *http.Request, presented string,
)

StartInboundRun answers POST /automation/inbound/{token} - the second route in this API that carries no bearer credential (automation.md §1.1).

What happens here is parsing, and nothing else. The shape of the token is a string question and belongs to an adapter; whether a rule has this address, whether it is enabled and whether its trigger is still an inbound webhook are all decided inwards of here (ADR-0005). Every one of those answers looks the same from out here, which is the point: a route that distinguished them would answer questions for whoever is trying tokens (T-21).

func (*RestController) StartJumbleIntake

func (c *RestController) StartJumbleIntake(
	w http.ResponseWriter, r *http.Request, presented string,
)

StartJumbleIntake answers POST /jumble/inbound/{token}.

func (*RestController) StartOidcSignIn

func (c *RestController) StartOidcSignIn(w http.ResponseWriter, r *http.Request)

StartOidcSignIn answers POST /auth/oidc:start.

func (*RestController) StartPasskeyEnrollment

func (c *RestController) StartPasskeyEnrollment(w http.ResponseWriter, r *http.Request)

StartPasskeyEnrollment answers POST /auth/mfa/passkey:enroll-options. Public, for ConfirmTotp's reason: the ENROLL credential in the body is what authenticates the call.

func (*RestController) StartPasskeyRegistration

func (c *RestController) StartPasskeyRegistration(
	w http.ResponseWriter, r *http.Request, params openapi.StartPasskeyRegistrationParams,
)

StartPasskeyRegistration answers POST /accounts/me/passkeys:start.

func (*RestController) StartPasskeySignIn

func (c *RestController) StartPasskeySignIn(w http.ResponseWriter, r *http.Request)

StartPasskeySignIn answers POST /auth/sessions:passkey-options.

func (*RestController) StartPasskeyStepUp

func (c *RestController) StartPasskeyStepUp(w http.ResponseWriter, r *http.Request)

StartPasskeyStepUp answers POST /auth/step-up:passkey-options.

func (*RestController) StartProviderConnect

func (c *RestController) StartProviderConnect(
	w http.ResponseWriter, r *http.Request, params openapi.StartProviderConnectParams,
)

StartProviderConnect answers POST /accounts/me/identities:connect.

func (*RestController) StartProviderStepUp

func (c *RestController) StartProviderStepUp(w http.ResponseWriter, r *http.Request)

StartProviderStepUp answers POST /auth/step-up:provider (ADR-0075 §2).

func (*RestController) StartRestore

func (c *RestController) StartRestore(w http.ResponseWriter, r *http.Request)

StartRestore answers POST /restores.

202 with a JobRef, because a restore takes minutes: the rows are written by a worker, and what the caller gets now is something to poll. `result_url` names the restore run, which carries the report the dry run produced.

func (*RestController) StepUp

func (c *RestController) StepUp(w http.ResponseWriter, r *http.Request)

StepUp answers POST /auth/step-up. Written out for DisableTotp's reason.

func (*RestController) StreamChanges

func (c *RestController) StreamChanges(
	w http.ResponseWriter, r *http.Request, params openapi.StreamChangesParams,
)

StreamChanges opens the change stream. Delegated rather than embedded, so that the field being nil is an answer rather than a panic.

func (*RestController) SubmitJumbleEntry

func (c *RestController) SubmitJumbleEntry(
	w http.ResponseWriter, r *http.Request, _ openapi.SubmitJumbleEntryParams,
)

SubmitJumbleEntry answers POST /jumble/entries.

func (*RestController) SuggestDecomposition

func (c *RestController) SuggestDecomposition(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId,
	_ openapi.SuggestDecompositionParams,
)

SuggestDecomposition answers POST /items/{itemId}:decompose.

202 and the job: the provider has not been asked yet, so the job is all there is to answer with.

func (*RestController) SuggestDuplicates

func (c *RestController) SuggestDuplicates(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId,
)

SuggestDuplicates answers POST /items/{itemId}:duplicates.

200 with the suggestion, or 204 when there is nothing to propose - which is also the answer with no pgvector, with no provider that can embed, and for an entry the embedding pass has not reached. The adapter cannot tell those apart and does not try: they are one answer to the caller, and which one it is is `/meta/capabilities`' business.

func (*RestController) SuggestFromJumbleEntry

func (c *RestController) SuggestFromJumbleEntry(
	w http.ResponseWriter, r *http.Request, entryID openapi_types.UUID,
	_ openapi.SuggestFromJumbleEntryParams,
)

SuggestFromJumbleEntry answers POST /jumble/entries/{entryId}:suggest.

202 and the job: the provider has not been asked yet, so the job is all there is to answer with. The proposal appears under /suggestions when it has.

func (*RestController) SuspendTenant

func (c *RestController) SuspendTenant(
	w http.ResponseWriter, r *http.Request, tenantID openapi.AdminTenantId,
)

SuspendTenant answers POST /admin/tenants/{tenantId}:suspend.

func (*RestController) SyncPull

func (c *RestController) SyncPull(w http.ResponseWriter, r *http.Request)

SyncPull answers one page of the delta. Delegated rather than embedded, so that the field being nil is an answer rather than a panic.

func (*RestController) SyncPush

func (c *RestController) SyncPush(w http.ResponseWriter, r *http.Request)

SyncPush applies a device's queue, delegated for the same reason.

func (*RestController) SyncSnapshot

func (c *RestController) SyncSnapshot(w http.ResponseWriter, r *http.Request)

SyncSnapshot streams the initial synchronisation, delegated for the same reason.

func (*RestController) TakeBackKudos

func (c *RestController) TakeBackKudos(w http.ResponseWriter, r *http.Request, itemID openapi.ItemId)

TakeBackKudos answers DELETE /items/{itemId}/kudos.

func (*RestController) TestBackupTarget

func (c *RestController) TestBackupTarget(
	w http.ResponseWriter, r *http.Request, targetID openapi_types.UUID,
)

TestBackupTarget answers POST /backup-targets/{targetId}:test.

200 with a result even when the target could not be reached: "it does not work and here is the code" is what the caller asked to find out, and a 502 would say this server failed instead.

func (*RestController) TestInstanceBackupTarget

func (c *RestController) TestInstanceBackupTarget(
	w http.ResponseWriter, r *http.Request, targetID openapi_types.UUID,
)

TestInstanceBackupTarget answers POST /admin/backup-targets/{targetId}:test.

func (*RestController) TestRule

func (c *RestController) TestRule(w http.ResponseWriter, r *http.Request)

TestRule answers POST /automation/rules:test.

func (*RestController) TrashContainer

func (c *RestController) TrashContainer(
	w http.ResponseWriter, r *http.Request,
	containerID openapi.ContainerId, params openapi.TrashContainerParams,
)

TrashContainer answers DELETE /containers/{containerId}.

A DELETE rather than an action suffix, and 204 rather than the container: it is gone from every list the client draws, and there is nothing about it left worth sending back. That it is a soft delete is the server's business - what the client has to know is where to find it again, which is the trash rather than this response (api-guidelines.md §2).

func (*RestController) TrashWorkItem

func (c *RestController) TrashWorkItem(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId, params openapi.TrashWorkItemParams,
)

TrashWorkItem answers DELETE /items/{itemId}.

A DELETE rather than an action suffix, and 204 rather than the entry: the entry is gone from every list the client draws, and there is nothing about it left worth sending back. That it is a soft delete is the server's business - what the client has to know is where to find it again, which is the trash rather than this response (api-guidelines.md §2).

The subtree goes with it. Nothing here says so, because nothing here decides it: the invariant is the application layer's, and a controller that mentioned the cascade would be a second place stating a rule.

func (*RestController) TriggerRuleManually

func (c *RestController) TriggerRuleManually(
	w http.ResponseWriter, r *http.Request, ruleID openapi.RuleId,
	_ openapi.TriggerRuleManuallyParams,
)

TriggerRuleManually answers POST /automation/rules/{ruleId}:trigger.

202 rather than 200: the run happens on a worker, and what comes back is the identifier it will carry rather than the run itself. A caller that wants the outcome watches GET /automation/runs/{runId}, which answers 404 until a worker claims the job.

func (*RestController) TryVoiceSentence

func (c *RestController) TryVoiceSentence(w http.ResponseWriter, r *http.Request)

TryVoiceSentence answers POST /voice:try.

func (*RestController) UnarchiveContainer

func (c *RestController) UnarchiveContainer(
	w http.ResponseWriter, r *http.Request,
	containerID openapi.ContainerId, _ openapi.UnarchiveContainerParams,
)

UnarchiveContainer answers POST /containers/{containerId}:unarchive.

func (*RestController) UnarchiveWorkItem

func (c *RestController) UnarchiveWorkItem(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId, _ openapi.UnarchiveWorkItemParams,
)

UnarchiveWorkItem answers POST /items/{itemId}:unarchive.

func (*RestController) UnassignWorkItem

func (c *RestController) UnassignWorkItem(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId,
	params openapi.UnassignWorkItemParams,
)

UnassignWorkItem answers POST /items/{itemId}:unassign. No body: there is nothing to say beyond which entry, since an entry has one assignee and taking them off names nobody.

func (*RestController) UpdateAccountPreferences

func (c *RestController) UpdateAccountPreferences(w http.ResponseWriter, r *http.Request, accountID openapi.AccountId)

UpdateAccountPreferences answers PATCH /accounts/{accountId}/preferences.

func (*RestController) UpdateBackupSchedule

func (c *RestController) UpdateBackupSchedule(
	w http.ResponseWriter, r *http.Request,
	scheduleID openapi_types.UUID, params openapi.UpdateBackupScheduleParams,
)

UpdateBackupSchedule answers PATCH /backup-schedules/{scheduleId}.

func (*RestController) UpdateBucket

UpdateBucket answers PATCH /containers/{containerId}/buckets/{bucketId}.

The collection in the path is not passed on. The column already knows which board it is on, and a path segment that could disagree with the row would be a second answer to a question that has one - the route names it so that a client can address a column without holding a flat identifier space in its head.

func (*RestController) UpdateContainerPolicies

func (c *RestController) UpdateContainerPolicies(
	w http.ResponseWriter, r *http.Request,
	containerID openapi.ContainerId, params openapi.UpdateContainerPoliciesParams,
)

UpdateContainerPolicies answers PUT /containers/{containerId}/policies.

A PUT rather than a merge patch, so presence is not read: a key the caller omitted is the default rather than what happens to be stored. Passing the field through unconditionally is what makes that true - the catalogue reads an absent `completion_policy` as MANUAL, and a handler that omitted the key when the client did would leave the use case unable to tell "not sent" from "sent as the default", which is a distinction this operation deliberately does not have.

func (*RestController) UpdateCustomField

func (c *RestController) UpdateCustomField(
	w http.ResponseWriter, r *http.Request, fieldID openapi.CustomFieldId,
	params openapi.UpdateCustomFieldParams,
)

UpdateCustomField answers PATCH /custom-fields/{fieldId}.

func (*RestController) UpdateDataSubjectRequest

func (c *RestController) UpdateDataSubjectRequest(
	w http.ResponseWriter, r *http.Request, requestID openapi_types.UUID,
)

UpdateDataSubjectRequest answers PATCH /privacy/requests/{requestId}.

func (*RestController) UpdateGroup

func (c *RestController) UpdateGroup(w http.ResponseWriter, r *http.Request, groupID openapi.GroupId, params openapi.UpdateGroupParams)

UpdateGroup answers PATCH /groups/{groupId}.

func (*RestController) UpdateLabel

UpdateLabel answers PATCH /containers/{containerId}/labels/{labelId}.

The collection in the path is not passed on, for the reason a bucket's is not: the label already knows which vocabulary it belongs to, and a path segment that could disagree with the row would be a second answer to a question that has one.

func (*RestController) UpdateOwnAccount

func (c *RestController) UpdateOwnAccount(w http.ResponseWriter, r *http.Request)

UpdateOwnAccount answers PATCH /accounts/me: the caller's own display name.

Written out like GetOwnAccount, for the reason given there. A field left out of the merge patch is left out of the input, so the use case leaves it.

func (*RestController) UpdatePlatformSubscription

func (c *RestController) UpdatePlatformSubscription(
	w http.ResponseWriter, r *http.Request, subscriptionID openapi_types.UUID,
	params openapi.UpdatePlatformSubscriptionParams,
)

UpdatePlatformSubscription answers PATCH /admin/subscriptions/{subscriptionId}: a merge patch, If-Match as the version, the step-up in its header.

func (*RestController) UpdateReminder

func (c *RestController) UpdateReminder(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId,
	reminderID openapi.ReminderId, params openapi.UpdateReminderParams,
)

UpdateReminder answers PATCH /items/{itemId}/reminders/{reminderId}.

func (*RestController) UpdateRetentionPolicy

func (c *RestController) UpdateRetentionPolicy(
	w http.ResponseWriter, r *http.Request,
	policyID openapi_types.UUID, params openapi.UpdateRetentionPolicyParams,
)

UpdateRetentionPolicy answers PATCH /retention-policies/{policyId}.

func (*RestController) UpdateRule

func (c *RestController) UpdateRule(
	w http.ResponseWriter, r *http.Request, ruleID openapi.RuleId,
)

UpdateAutomationRule answers PATCH /automation/rules/{ruleId}.

func (*RestController) UpdateSavedView

func (c *RestController) UpdateSavedView(
	w http.ResponseWriter, r *http.Request, viewID openapi.ViewId, params openapi.UpdateSavedViewParams,
)

UpdateSavedView answers PATCH /views/{viewId}. A merge patch: a member that is absent is left alone, and the query and the hints replace whole - a query is one statement.

func (*RestController) UpdateTemplate

func (c *RestController) UpdateTemplate(
	w http.ResponseWriter, r *http.Request, templateID openapi.TemplateId,
	params openapi.UpdateTemplateParams,
)

UpdateTemplate answers PATCH /templates/{templateId}.

func (*RestController) UpdateTenant

func (c *RestController) UpdateTenant(
	w http.ResponseWriter, r *http.Request, tenantID openapi.AdminTenantId, params openapi.UpdateTenantParams,
)

UpdateTenant answers PATCH /admin/tenants/{tenantId}: a platform's reference, set or cleared.

func (*RestController) UpdateTenantQuotas

func (c *RestController) UpdateTenantQuotas(
	w http.ResponseWriter, r *http.Request, tenantID openapi.AdminTenantId,
)

UpdateTenantQuotas answers PATCH /admin/tenants/{tenantId}/quotas. The body is read as a raw document rather than the generated struct: "absent", "null" and "0" are three different instructions, and a Go pointer can only carry two of them.

func (*RestController) UpdateWebhookSubscription

func (c *RestController) UpdateWebhookSubscription(
	w http.ResponseWriter, r *http.Request, webhookID openapi.WebhookId,
	params openapi.UpdateWebhookSubscriptionParams,
)

UpdateWebhookSubscription answers PATCH /integrations/webhooks/{webhookId}.

func (*RestController) UpdateWorkItem

func (c *RestController) UpdateWorkItem(
	w http.ResponseWriter, r *http.Request, itemID openapi.ItemId, params openapi.UpdateWorkItemParams,
)

UpdateWorkItem answers PATCH /items/{itemId}.

A JSON Merge Patch (RFC 7386), which is why presence is read rather than the value alone: an absent `notes` means "leave them alone" and `"notes": null` means "clear them", and both arrive as a nil pointer in the generated struct. A handler that could not tell them apart would clear the notes of every client that only meant to rename something.

Null reaches the catalogue as the empty string. The domain holds `notes` as a string whose empty value is "none", so cleared and empty are one state there - and inventing a second spelling for it in this layer would be a distinction only this layer believed in.

func (*RestController) UpdateWorkspace

func (c *RestController) UpdateWorkspace(
	w http.ResponseWriter, r *http.Request, params openapi.UpdateWorkspaceParams,
)

UpdateWorkspace answers PATCH /tenant.

func (*RestController) UploadMediaContent

func (c *RestController) UploadMediaContent(
	w http.ResponseWriter, r *http.Request, mediaID openapi.MediaId, params openapi.UploadMediaContentParams,
)

UploadMediaContent answers PUT /media/{mediaId}:content.

No bearer credential and no actor: the URL is the capability, exactly as a presigned bucket URL is, and the token in it is what this handler verifies. Everything after that verification is the application layer's - which tenant the transaction runs as comes out of the token, and what may be done with the object is decided there (ADR-0005).

func (*RestController) VerifyAuditChain

func (c *RestController) VerifyAuditChain(w http.ResponseWriter, r *http.Request)

VerifyAuditChain answers POST /audit:verify.

func (*RestController) VerifyBackup

func (c *RestController) VerifyBackup(
	w http.ResponseWriter, r *http.Request, backupID openapi_types.UUID,
)

VerifyBackup answers POST /backups/{backupId}:verify.

func (*RestController) WithdrawConsent

func (c *RestController) WithdrawConsent(w http.ResponseWriter, r *http.Request)

WithdrawConsent answers POST /privacy/consents:withdraw.

func (*RestController) WithdrawInstallationPreset

func (c *RestController) WithdrawInstallationPreset(
	w http.ResponseWriter, r *http.Request, presetID openapi.PresetId, params openapi.WithdrawInstallationPresetParams,
)

WithdrawInstallationPreset answers DELETE /admin/presets/{presetId}.

func (*RestController) WithdrawInstanceIdentityProvider

func (c *RestController) WithdrawInstanceIdentityProvider(
	w http.ResponseWriter, r *http.Request, providerID openapi.ProviderId,
	params openapi.WithdrawInstanceIdentityProviderParams,
)

WithdrawInstanceIdentityProvider answers POST /admin/identity-providers/{providerId}:withdraw.

The body is optional: none is the default notice. Read whole and decoded only when something arrived, so an empty request is the fourteen days rather than a malformed one.

func (*RestController) WithdrawPreset

func (c *RestController) WithdrawPreset(
	w http.ResponseWriter, r *http.Request, presetID openapi.PresetId, params openapi.WithdrawPresetParams,
)

WithdrawPreset answers DELETE /presets/{presetId}.

func (*RestController) WriteInstanceSettings

func (c *RestController) WriteInstanceSettings(
	w http.ResponseWriter, r *http.Request, params openapi.WriteInstanceSettingsParams,
)

WriteInstanceSettings answers PUT /admin/settings.

type Router

type Router interface {
	http.Handler
	// Handler resolves a request to its handler and the pattern that matched, without serving.
	Handler(r *http.Request) (http.Handler, string)
}

Router is what the observability middleware needs beyond serving: the route template, known before dispatch. *http.ServeMux satisfies it.

type Secured

type Secured struct {
	Next http.Handler
	CORS env.CORSConfig
}

Secured adds the response headers of security.md §9 and answers CORS preflights.

It is the outermost middleware after the observability wrapper, because the headers have to be on every answer - including the ones no handler produced, such as a 404 from the router or a 429 from the rate limiter.

func (Secured) ServeHTTP

func (s Secured) ServeHTTP(w http.ResponseWriter, r *http.Request)

type Shedder

type Shedder func(deferrable bool) (release func(), err error)

Shedder decides whether one call may run, and is what the composition root closes over.

A function rather than the load shedder itself: the shedder is infrastructure and an inbound adapter does not reach across to the outbound side (project-structure.md §2). The contract is that release is never nil - including on a refusal, so that a caller may defer it unconditionally.

type Shedding

type Shedding struct {
	Next http.Handler
	// Routes resolves the route template, which is what decides the class. The template rather
	// than the path: the class is a property of the operation, and two items are the same
	// operation.
	Routes Router
	// Admit is nil when shedding is switched off, and then this middleware is a pass-through
	// rather than a threshold nobody can reach.
	Admit Shedder
	// Signals counts the refusals, under the same instrument the rate limits use and with the
	// class in the scope - `load_shed:deferrable`. One counter for every way a call is turned
	// away, because the question an operator asks under load is how much is being refused, and
	// an answer split across instruments is one nobody assembles. Optional; nil records nothing.
	Signals LimitSignals
}

Shedding is admission control in front of the API: above a threshold on the requests in flight, deferrable work is refused with 503 and a Retry-After before latency tips over for everyone (observability-reliability.md §6, RT-6).

It sits above authentication and above the rate limits, and below the body bound. Above, because a refusal must cost nothing - a shed request that had already spent a database lookup resolving its actor would be load rather than the absence of it. Below, because an oversized body is a cheaper refusal still and belongs first.

func (Shedding) ServeHTTP

func (s Shedding) ServeHTTP(w http.ResponseWriter, r *http.Request)

type Snapshotter

type Snapshotter interface {
	WalkAll(ctx context.Context, actor appshared.ActorContext, request syncservice.SnapshotRequest,
		emit func(syncservice.Record) error) (syncservice.Position, error)
	Encode(position syncservice.Position) string
}

Snapshotter is the slice of the pull service the snapshot drives: the whole walk, handed out record by record, and how the cursor at its end is spelled.

type StreamController

type StreamController struct {
	Stream   Changes
	Registry *stream.Registry
	Wakeups  Wakeups
	Signals  StreamSignals
	// Clock is injectable so the tests do not have to wait. Nil means the system clock.
	Clock func() time.Time
}

StreamController serves `GET /stream` as server-sent events.

The handler is the connection: it runs for as long as the stream lives, and it ends by returning - which is what lets the HTTP server's own drain wait for it. Nothing here starts a goroutine, and that is deliberate rather than incidental: a stream that spawned a writer would need the writer to be told about every way a connection can end, and net/http already owns three of them.

func (StreamController) StreamChanges

func (c StreamController) StreamChanges(
	w http.ResponseWriter, r *http.Request, params openapi.StreamChangesParams,
)

StreamChanges opens the stream.

type StreamSignals

type StreamSignals interface {
	StreamOpened(ctx context.Context)
	StreamClosed(ctx context.Context, seconds float64)
	StreamRefused(ctx context.Context, reason string)
	StreamRecords(ctx context.Context, count int)
}

StreamSignals is the slice of the metrics adapter the stream reports through (observability-reliability.md §3.2). The labels are closed sets: a refusal reason, never an identifier.

type SyncController

type SyncController struct {
	Pull    Puller
	Signals SyncSignals
	// Push serves `POST /sync:push`. Nil leaves the route answering the pending 404.
	Push        Pusher
	PushSignals PushSignals
	// Registry admits a snapshot's connection beside the streams', and StreamSignals
	// counts it with them. Nil leaves `POST /sync:snapshot` answering not wired.
	Registry      *stream.Registry
	StreamSignals StreamSignals
}

SyncController serves `POST /sync:pull`. Not a catalogue entry, for the stream's reason: a pull is a connection served in pages rather than an action a rule or an agent could invoke.

func (SyncController) SyncPull

func (c SyncController) SyncPull(w http.ResponseWriter, r *http.Request)

SyncPull answers one page of the delta.

func (SyncController) SyncPush

func (c SyncController) SyncPush(w http.ResponseWriter, r *http.Request)

SyncPush applies a device's queue.

func (SyncController) SyncSnapshot

func (c SyncController) SyncSnapshot(w http.ResponseWriter, r *http.Request)

SyncSnapshot answers POST /sync:snapshot.

type SyncSignals

type SyncSignals interface {
	PullRecords(ctx context.Context, count int)
}

SyncSignals is the slice of the metrics adapter the pull reports through. Records delivered, beside the stream's own counter: against the change log's growth the two together say whether devices are keeping up, and apart they say by which door.

type TokenAuthenticator

type TokenAuthenticator interface {
	Execute(context.Context, usecase.AuthenticateTokenCommand) (appshared.ActorContext, error)
}

TokenAuthenticator is the slice of the authentication use case this middleware needs. An interface rather than the handler, so that the middleware can be tested without a database and the presentation layer keeps pointing inwards.

type UseCaseRegistry

type UseCaseRegistry interface {
	Invoke(ctx context.Context, name string, actor appshared.ActorContext, in usecase.Input) (usecase.Output, error)
}

UseCaseRegistry is the catalogue as this layer needs it: run the operation named, with the actor of the request and the input the body carried.

An interface rather than *usecase.Registry so that a controller test needs no wiring, and because this is the whole of what the REST layer is allowed to do with the catalogue - it executes entries, it does not add any.

type Wakeups

type Wakeups interface {
	Subscribe(tenantID shared.ID) (<-chan struct{}, func())
}

Wakeups tells the stream when a workspace has changed, so that a round happens because something happened rather than because a timer fired (ADR-0007).

Source Files

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL