Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

RFC-0003: Teams, ownership, and policy-based authorization

Version: 0.1.0 | Status: normative | Phase: spec Owners: @Will Eaton


1. Summary

[RFC-0003:C-CONTEXT] Context and goals (Informative)

The controller authorizes today through three unrelated mechanisms: a global two-tier role ladder configured by login lists, principal ownership on secrets and personal dispatch targets, and a source-forge approver allowlist. Ownership is recorded on standing launches but never consulted on edit or delete, a team cannot own a schedule, ownership alone never suffices without a global role, every read surface is open to any authenticated caller, and an agent inside a run acts with its launcher's full authority. This RFC defines one authorization contract: an open principal registry that adds controller-managed teams (composed of users, groups, rules, and other teams) and run principals; an owner on every governed resource with per-resource shares and a parent relation; a single deny-by-default decision bound to every route; and a policy set with a published schema that expresses roles, ownership rules, shares, expiry, dispatch admission, and approver sets as data. The decisions that define the secrets registry and brought clusters will be amended to defer to it on who may act. It does not prescribe a policy engine, a storage layout, an evaluation architecture, or a user interface framework; those choices belong in decision records that reference this RFC.

Since: v0.1.0


2. Specification

[RFC-0003:C-PRINCIPALS] Principals (Normative)

A principal is <scheme>:<id>. Schemes are registered: this RFC registers user, group, team, and run, and a later amendment MAY register more. The policy schema of RFC-0003:C-POLICY MUST be versioned over the registered schemes, and a principal whose scheme is not registered MUST deny. user:<login> and team:<slug> MUST be stored and compared lowercased and trimmed. group:<path> MUST be stored and compared exactly as the identity layer normalizes it, applied identically at write and at comparison, so that distinct identity-provider groups are never merged by the controller. A team slug MUST match [a-z0-9][a-z0-9-]{1,62} and MUST be unique per controller. User and group principals MUST derive from a controller-validated identity (the session, token, or key the controller itself verified); a caller MUST NOT be able to assert a group by supplying it in a request. Team principals MUST derive from the controller's own team membership records; run principals from RFC-0003:C-RUN-PRINCIPAL. Every request has a subject, the principal set the decision evaluates, and an actor, the principal that caused the request: the authenticated user for an interactive or API-key call, the submitter when a portal records one, otherwise the owner principal of the launch or standing launch that fired. Subject and actor coincide for interactive and API-key calls unless the caller acts as a team under RFC-0003:C-ACT-AS, and differ for firings, portal submissions, and runs. The subject set is the subject's own principal, every group principal the credential proves, and every team principal whose membership those principals satisfy under RFC-0003:C-TEAMS. The group refresh window is the configured interval at which the identity layer re-reads a credential's groups from the identity provider; a downgraded session is one whose refresh the provider refused. A credential that cannot prove groups (a static token, a cluster token, a downgraded session) MUST act as its user principal plus the team memberships whose derivation contains no group or rule member, including through nested teams, and MUST NOT act as any group-derived or rule-derived membership.

Since: v0.1.0

[RFC-0003:C-TEAMS] Teams (Normative)

A team is a controller-managed record with a unique slug, a display name, and a member set. Each member MUST be a user principal, a group principal, another team, or a membership rule, paired with a team role. Team roles are totally ordered member < maintainer < owner; an amendment adding a role MUST place it in this order. A membership rule is a predicate over attributes of a controller-validated identity (at least: email domain, and group path prefix); it MUST be evaluated only against attributes the identity layer verified, and a change to a rule MUST be audited like a change to a listed member. Team membership MUST form a directed acyclic graph and a membership that would close a cycle MUST be refused. Along a nesting path the conferred role is the minimum of the roles on that path; a user's role in a team is the maximum over all paths that reach them. Group-derived and rule-derived membership MUST reflect the identity provider within one group refresh window. A team is itself a governed resource whose owner principal is the team; a team is not transferable and transfer on a team MUST deny. On creation the request MUST list at least one member at the owner role that currently resolves to a user; absent an explicit list, the creator is listed at owner. Creating a team, changing its members, rules, or roles, renaming it, and deleting it MUST be authorized by the decision in RFC-0003:C-DECISION and audited as RFC-0003:C-AUDIT requires. A team MUST NOT be deleted while it owns any other governed resource; the refusal MUST name the blocking resources the caller may read and the count of any others. A team none of whose memberships currently resolve to any user MUST remain the owner of its resources and MUST be listed as unreachable in the team listing available to platform administrators. Platform administration MUST be a team, so that administrator membership can be held through a group and changed without a redeploy.

Since: v0.1.0

[RFC-0003:C-OWNERSHIP] Ownership of governed resources (Normative)

Every governed resource MUST carry exactly one owner principal that is a user or a team. The governed resources are: registered playbooks, playbook drafts, pack imports, autoresearch scopes, standing launches and their triggers (schedules, watches, webhooks, portals), one-shot launches, runs, secrets and their bindings, model providers and dispatch defaults scoped below the platform, dispatch targets including brought clusters, API keys, policy sets, teams, and every child resource named in RFC-0003:C-HIERARCHY. An API key's owner MUST be the user it acts as. For a root resource the owner MUST be chosen by the creator from the principals the request acts as, defaulting to the creator's user principal; a creator MUST NOT set an owner they do not act as, and acting as the intended owner is necessary and not sufficient: create on that owner is still decided by policy. For a child resource the owner is its parent's owner and is not chosen; create on the child is decided against the parent's owner and scope. The owner MUST NOT change except through the transfer action on a root resource. Ownership MUST be transferable only to a principal the transferring caller acts as or, for a platform administrator, to any user or team; a transfer that would collide with an identifier the new owner already holds under RFC-0003:C-NAMES MUST be refused; and every transfer MUST be audited. A resource whose owner is a team MUST remain usable when any individual member leaves; a resource whose owner is a user MUST remain visible to platform administrators so it can be transferred when the user departs.

Since: v0.1.0

[RFC-0003:C-ACTIONS] Action vocabulary (Normative)

Authorization MUST be expressed over a fixed, documented action vocabulary per resource type, using stable identifiers of the form <resource>:<verb>. Every resource type MUST define at least read, create, update, delete, transfer, and share; playbooks, drafts, and standing launches MUST additionally define launch; imports, scopes, and drafts (graduation) MUST define approve; secrets MUST define bind and rotate; dispatch targets MUST define dispatch; runs MUST define publish and approve, answering a decision request the run opened under RFC-0002:C-HUMAN-DECISION; drafts MUST define publish, registration into the registry without review, which a rule deriving from ownership alone MUST NOT grant; policy sets MUST define activate; teams MUST define manage-members. transfer on a child resource resolves to transfer on its root under RFC-0003:C-HIERARCHY. The vocabulary MUST be retrievable from the API by any authenticated user principal. Adding an action is a compatible change; renaming or removing one is a breaking change under the RFC lifecycle. A request naming an action or resource type outside the vocabulary MUST be refused.

Since: v0.1.0

[RFC-0003:C-DECISION] One authorization decision (Normative)

Every API operation MUST be authorized by one decision that takes: the subject principal set and the team role held under each team principal in it; the actor; one action from RFC-0003:C-ACTIONS; the resource type, the resource's owner principal, its ancestor chain under RFC-0003:C-HIERARCHY, and where the action is dispatch the resolved target (or, for create and listing, the resource type with the intended owner and parent); the active policy set; the shares on the resource and its ancestors; and the current time. It MUST answer allow or deny with a structured reason naming the rule that decided or the absence of one. The decision MUST be deny by default: an empty subject set, an action outside the vocabulary, or an unresolvable resource MUST deny. The same decision MUST serve the browser, the API-key path, the MCP surface, run principals, and non-interactive re-checks, so that what a preview shows, what a launch is refused with, and what a firing standing launch may do agree. A standing launch, watch, webhook, or portal MUST fire with its owner as subject and that owner's memberships resolved at firing time, never from a stored snapshot; when the owner is a team, the firing's subject is that team principal at the maintainer role. The actor of a firing is the submitter when one exists, otherwise the owner. A firing whose owner cannot be resolved (a deleted user, a team with no resolvable member) or whose decision denies MUST be refused, handled under RFC-0001:C-RECURRING-POLICY, and audited. An update that changes what a standing launch fires (its playbook, inputs, resolved target, or bindings) MUST additionally be decided as a launch by the updating subject, who MUST hold launch on the playbook, dispatch on the resolved target, and bind on each bound secret, or the update MUST be refused. Roles, ownership, team membership, shares, and approver sets MUST all be inputs to this one decision; an endpoint MUST NOT consult an independent allowlist, environment list, or role check instead of it. Disclosure of a denial is governed by RFC-0003:C-READ-SCOPING.

Since: v0.1.0

[RFC-0003:C-ENFORCEMENT] Enforcement at the boundary (Normative)

Every served route MUST be bound to exactly one action and one resource resolution, and a request to a route with no binding MUST be denied for every caller. The route-to-action bindings MUST be retrievable so that conformance can be checked against the served route table. A response MUST NOT reflect any effect of a request the decision denied. A list MAY be narrowed only by applying the same decision per item. Machine surfaces that authenticate by a pod-bound credential rather than a principal (a pod token's evidence uploads under RFC-0004:C-RUN-EVIDENCE, metrics) and the webhook delivery route, which authenticates by a per-webhook secret under RFC-0003:C-WEBHOOK-DELIVERY, are outside this contract and MUST NOT be granted any action from RFC-0003:C-ACTIONS. Routes that manage webhooks or read their deliveries are served routes under this clause.

Since: v0.1.0

[RFC-0003:C-POLICY] Policy as stored, validated data (Normative)

The rules the decision evaluates MUST be a policy set with a published schema over the principal, action, and resource vocabularies of this RFC. A policy set MUST be validated against that schema before it becomes active, and an invalid set MUST be refused with the failing rule named. Validation MUST also refuse a set that grants manage-members or transfer on a team to a role below owner, and a set under which no user or team principal would hold activate. The active policy set MUST be identified by a content digest, and the policy set is a governed resource read through the read action, which the default policy set MUST grant to all authenticated user principals. The controller MUST ship a default policy set that grants: a user owner every action on the resources they own; on a team-owned resource, the team's member role read and launch, the maintainer role additionally create, update, bind, and rotate, and the owner role every action including transfer, share, delete, and manage-members; every authenticated user principal dispatch on a dispatch target owned by the platform administrator team, matching the open default of the cluster policy it replaces; and the platform administrator team every action on every resource. Activating a policy set is the activate action, decided by the policy in force at that moment, and MUST be audited with the prior and new digests.

Since: v0.1.0

[RFC-0003:C-APPROVALS] Approvers as policy (Normative)

Who may approve a pack import, a scope, a draft graduation, or a run's decision request MUST be decided by the approve action through RFC-0003:C-DECISION, with approver sets expressed as team roles or policy rules rather than a separate allowlist. An approval recorded from an external system (a source-forge review) MUST be attributed to a controller identity through a verified account link: an association between the external login and a controller user that the user established while authenticated to both. An external login with no verified link MUST NOT approve. Any existing approver allowlist or forge-association fallback MUST be honoured only through an explicit policy rule that names it, so that removing the rule removes the path.

An approval of a draft graduation MUST record the draft and the digest it approves (RFC-0002:C-PACK-BASE) and MUST apply to every version of that draft whose digest equals the approved digest, and to no other. An approval recorded for any other resource, a base included, MUST NOT be treated as an approval of it, even when the digests are equal. The approval request for a pack saved on a base MUST show the base digest, each registered or imported pack the approver may read under RFC-0003:C-READ-SCOPING that has that digest as its current revision, every overlay path, and which of those paths replaced a base path. A scope approval binds the exposure extracted from the pack and is not changed by this paragraph.

The default policy set MUST grant approve on a run to the owner of the launch that produced it, and to the members holding at least the maintainer role where that owner is a team; a policy MAY narrow or widen the set. approve on a run MUST NOT be granted to any run principal, by the default policy set or by any rule: an approval the gated run could give itself guards nothing. An answer to a decision request MUST be bound to the evidence digest it names under RFC-0002:C-DECISION-EVIDENCE, and to no other evidence.

Since: v0.1.0

[RFC-0003:C-READ-SCOPING] Read scoping (Normative)

Reading a governed resource is the read action and MUST be authorized like any other. A list endpoint MUST return only the items the caller may read and MUST NOT reveal whether items were withheld. When a caller lacks read on a resource, every action on that resource MUST return the same response as for a resource that does not exist; the structured denial of RFC-0003:C-DECISION is returned only when the caller may read the resource. A child resource is read under its parent's scope as RFC-0003:C-HIERARCHY defines, and a resource's shares are read with it. Team memberships and the active policy set are governed resources read under the same rule.

Since: v0.1.0

[RFC-0003:C-CREDENTIAL-PARITY] Credential parity (Normative)

The decision MUST produce the same answer for the same user regardless of which credential proves them, provided the credential proves the same groups. An API key MUST act with the owner's group memberships as refreshed within the group refresh window that governs sessions; a key whose owner's groups cannot be refreshed within that window MUST act as the user principal plus directly listed team memberships until they can. Revoking a team membership or a group MUST take effect for every credential of the affected user within one group refresh window. The identity probe MUST state whether the presenting credential proves groups.

Since: v0.1.0

[RFC-0003:C-AUDIT] Audit (Normative)

The controller MUST record an audit event for: team creation, membership, rule, and role changes, and deletion; ownership assignment at creation and every transfer; share grant, change, and revocation; activation of a policy set; issuance of a run credential; every accepted answer to a decision request, with the evidence digest it names; and every denied mutation. Each record MUST carry the actor, the subject when it differs, the credential path that proved the request, the action, the resource type and identifier, the rule that decided, the prior and resulting state where the operation changed one, and the time. An operation whose audit record cannot be persisted MUST fail. Audit records MUST be retrievable by the owner of the affected resource, by the affected team's owners, and by platform administrators, under the read scoping of the resource they concern. This extends RFC-0001:C-AUDIT from administrator policy changes to authorization changes generally.

Since: v0.1.0

[RFC-0003:C-COMPATIBILITY] Compatibility and migration (Normative)

Adoption MUST NOT grant any principal an action the prior mechanisms denied, except grants the active policy set makes explicitly to user owners, team roles, and shares, and every such grant MUST be attributable to a policy rule. Existing governed resources MUST be backfilled with an owner in this precedence: the recorded owner snapshot if present, else the recorded creator, else the platform administrator team. A resource owned by a group:<path> principal MUST be migrated to a team whose sole member is that group at the owner role, created for the purpose when none exists; the team's slug is the group path's last segment lowercased with every character outside [a-z0-9-] replaced by -, suffixed with -2, -3, and so on when taken; the mapping MUST be audited. Existing administrator and operator login and group lists MUST be migrated into a platform administrator team and a platform operator team with policy rules granting the actions those roles gate today, as the route bindings of RFC-0003:C-ENFORCEMENT record them; the existing cluster policy MUST be migrated into dispatch rules under RFC-0003:C-CONDITIONS. A fresh install, or one whose platform administrator team has no membership that resolves to a user, MUST seed that team from configuration at startup and MUST NOT offer any request path that creates the first administrator. During migration a resource governed by both an existing guard and the new decision MUST be allowed only when both allow. The existing principal spellings user:<login> and group:<path> MUST remain valid and unchanged; team:<slug> and run:<id> are additive. The decisions that today define secret ownership and brought-cluster ownership will be amended to defer to this RFC on who may act; their custody, delivery, and probing content is untouched.

Since: v0.1.0

[RFC-0003:C-TEAM-UX] Team-centric views (Normative)

The user interface MUST present governed resources filtered by an owner context the caller chooses among the principals they act as, with each team and the caller's own user principal as a context, and MUST include resources shared with that context. Choosing a team as the owner context MUST act as that team under RFC-0003:C-ACT-AS; choosing the caller's own user principal or showing every owner MUST NOT. The owner picker on every creation surface MUST offer only the principals the caller acts as. A refusal MUST be shown with the reason the decision returned, verbatim. The interface MUST show the signed-in user's team memberships and roles as the controller resolves them, including memberships held through a group, a rule, or a nested team, and MUST show a resource's shares to anyone who may read it.

Since: v0.1.0

[RFC-0003:C-RUN-PRINCIPAL] Run principal (Normative)

Every run MUST act under its own principal run:<run id>, proven by a credential issued at dispatch that proves only that run, and MUST NOT act under its launcher's or owner's credential. The run principal's subject set is the run principal alone; its actor is the owner principal of the launch that produced it. The default policy set MUST grant a run principal exactly: read on its launch, on itself, and on their children under RFC-0003:C-HIERARCHY; and publish on itself, where publish is recording an output the launch's declared exposure permits under RFC-0001:C-EXPOSURE. Task logs, artifacts, events, and spend are recorded through the pod evidence routes that RFC-0003:C-ENFORCEMENT places outside this contract and are not actions of the run principal. A decision request under RFC-0002:C-HUMAN-DECISION is opened and polled with the run's credential, and its answer is read under read on the run. The grants of the policy set and vocabulary to authenticated user principals do not extend to run principals. A run credential MUST expire when the run reaches a terminal state and MUST be refused after that regardless of expiry. A request under a run principal for any resource outside its launch MUST return the not-found response of RFC-0003:C-READ-SCOPING.

Since: v0.1.0

[RFC-0003:C-SHARING] Per-resource shares (Normative)

A subject holding share on a governed resource MAY grant another principal a share role on that one resource. A share names the resource, the grantee principal, a share role from a fixed vocabulary of at least viewer (read), launcher (read and launch), and editor (read, update, launch, bind), and optionally a not-after time under RFC-0003:C-CONDITIONS. A share role confers only the actions the resource type defines. A share MUST NOT confer transfer, delete, share, approve, activate, or manage-members, and a grant of a role containing any action the sharer does not hold on the resource MUST be refused rather than clipped. A share is a resource-scoped rule stored with the resource, outside the digested policy set of RFC-0003:C-POLICY; the shares on a resource are part of its read. A share MUST be revocable by any subject holding share on the resource or by a platform administrator, and MUST be audited on grant, change, and revocation. Transferring a resource MUST preserve its shares. A share on a parent applies to its children under RFC-0003:C-HIERARCHY; a share is otherwise never inherited.

Since: v0.1.0

[RFC-0003:C-HIERARCHY] Resource hierarchy (Normative)

A governed resource MAY have exactly one parent resource, itself governed. A child MUST have the same owner as its parent, MUST inherit its parent's read scope and shares, and MUST NOT be transferred separately from its root. The parent relation is: a draft version's parent is its draft; a trigger's parent is its standing launch; a webhook delivery's parent is its webhook; a run's parent is the launch or standing launch that produced it; a run's task logs, artifacts, events, and spend records are children of the run; a binding's parent is its secret. Every spend record MUST carry, in addition to its inherited owner, the owner principal of its launch at the time of the spend; transfer changes the inherited owner and MUST NOT rewrite that attribution, so that spend sums by who spent it. Actions on a child that the vocabulary does not define for its type MUST resolve to the corresponding action on the nearest ancestor that defines it.

Since: v0.1.0

[RFC-0003:C-CONDITIONS] Rule conditions (Normative)

A policy rule or share MAY carry a not-after time; the decision MUST evaluate it against the controller's clock at decision time, and an expired rule or share MUST NOT allow anything. Expiry MUST be visible wherever the rule or share is listed. The dispatch action on a dispatch target MUST be decided per target through policy rules, so that which principals may dispatch onto which cluster or brought target is a policy fact and not a separate configuration; absent a rule, a target admits only its owner as RFC-0003:C-POLICY grants, and a platform-owned target admits every authenticated user principal by the default policy set. A launch or firing whose subject may not dispatch onto its resolved target MUST be refused before any pod exists. The policy schema MUST admit both conditions from its first version; other conditions are later amendments.

Since: v0.1.0

[RFC-0003:C-NAMES] Names are scoped to the owner (Normative)

The human-readable identifier of a governed root resource (a playbook id, a draft id, a secret name, a provider id, a target name) MUST be unique within its owner, and two resources with different owners MAY share an identifier. Team slugs are unique per controller as RFC-0003:C-PRINCIPALS requires. The API MUST address a governed resource by owner principal and identifier, with the owner principal carried as one percent-encoded path segment in its <scheme>:<id> spelling. The API MUST accept an unqualified identifier only where it resolves to exactly one resource the caller may read, and MUST refuse an ambiguous one as a conflict naming the count of candidates. Every existing resource keeps its identifier through the migration of RFC-0003:C-COMPATIBILITY, since assigning owners cannot create a collision within one owner.

Since: v0.1.0

[RFC-0003:C-ACT-AS] Acting as a team (Normative)

A caller MAY act as a team it holds a membership in by naming that team on each request. While acting as team T, the request's subject MUST be the team principal T at the lesser of the caller's role in T and maintainer: the subject a standing launch owned by T fires as under RFC-0003:C-DECISION, never above the role the caller holds. The caller's user principal, group principals, and other team memberships MUST NOT be in that subject set. The actor remains the caller, and an audit record MUST carry T as the subject. A request naming a team the caller does not hold MUST be refused and MUST NOT be answered as the caller's own subject. Only a team principal may be acted as. Every decision, read scope, and entitlement for the request MUST be made for that subject, except that managing the caller's own API keys, sign-in credential, and interface preferences acts for the caller, since those belong to the person rather than to any team. The interface MUST show the team the caller is acting as, and MUST show a refusal to act as a team with its reason.

Since: v0.1.0

[RFC-0003:C-WEBHOOK-DELIVERY] Webhook delivery surface (Normative)

A webhook is a trigger under RFC-0003:C-HIERARCHY whose firing is requested by a sender outside the controller; a standing launch has at most one webhook. The delivery address MUST carry an opaque webhook identifier and MUST NOT carry the owner principal or the playbook identifier. The delivery surface MUST authenticate every delivery by the webhook's own secret, using one verifier the owner chose from this set: an HMAC-SHA256 of the body in a named header, given as sha256= followed by the lowercase hex digest; a shared token in the delivery path. Adding a verifier is a compatible change; removing one is a breaking change under the RFC lifecycle. The surface MUST NOT record or act on a delivery its webhook's verifier does not prove. The body size limit MUST be the same for every webhook and MUST be applied before the webhook is looked up. Every delivery the surface does not record, whatever the cause (an unknown or deleted webhook, a disabled webhook, failed verification, or a body over the limit), MUST receive the same status code, body, and headers apart from per-response values such as the date and a request id. A presented secret MUST be compared in constant time. A refusal applied before the webhook is looked up and independent of its identifier, such as load shedding, MAY differ from that response. A delivery MUST be durably recorded before it is acknowledged. The acknowledgement MUST be the same whatever the delivery later settles to and MUST NOT wait for filtering, parameter derivation, or launch; a verified delivery that cannot be recorded MUST receive a response the sender can retry, distinct from the acknowledgement. The sender is not a principal and MUST NOT be granted any action from RFC-0003:C-ACTIONS.

Since: v0.1.0

[RFC-0003:C-WEBHOOK-SETTLEMENT] Webhook settlement (Normative)

Recorded deliveries to one webhook MUST settle in the order they were recorded, each to exactly one outcome of launched, filtered, duplicate, throttled, or failed, with the reason, and a delivery MUST NOT launch after it settles to any outcome but launched. Settlement MUST apply these steps in order, stopping at the first that settles: parsing the body as JSON (failed); the filter (filtered, or failed if it cannot be evaluated); deriving the deduplication key (failed); a consumed key (duplicate); the rate bound (throttled); deriving parameters and deciding the firing (failed); launching (launched). A webhook MUST launch only by firing its standing launch, decided under RFC-0003:C-DECISION as that standing launch's firing; a webhook firing has no submitter, and a firing the decision denies MUST settle failed with the decision's reason. A webhook MUST fire the playbook revision its standing launch pins and MUST NOT fire a draft, and a launch it mints MUST record the delivery that produced it. A key is consumed only by a delivery that settles launched, and a later delivery to the same webhook carrying a consumed key MUST settle duplicate for as long as the webhook exists; removing a delivery record MUST NOT release its key. Every webhook MUST carry a finite maximum number of launches in any 60-minute window, set by its owner and no greater than a platform maximum. Every value a webhook derives from a delivery MUST reach a run only as a parameter value that did not originate inside the pack under RFC-0002:C-PLAYBOOK-PARAMS; a run principal MUST NOT hold read on a webhook or a webhook delivery, whatever RFC-0003:C-RUN-PRINCIPAL grants on its launch's children. A settled delivery's outcome and reason, and its recorded body and headers, MUST be retrievable under the webhook's read scope for at least a retention period the webhook declares, no longer than a platform maximum, and are removed with the webhook. The recorded headers and address MUST exclude any header value or path segment that carries the webhook's secret or a previous secret.

Since: v0.1.0

[RFC-0003:C-WEBHOOK-LIFECYCLE] Webhook secrets and lifecycle (Normative)

A webhook's secret MUST be generated by the controller with at least 128 bits of entropy and disclosed once, in the response to the create or replacement that generated it; it MUST NOT be readable after that. A webhook's verifier, and the header it reads, MUST be fixed at creation; a save that changes either MUST be refused. Replacing the secret is update on the webhook and applies to deliveries received after the replacement; the controller MAY accept the previous secret for a declared overlap no longer than a platform maximum. No audit record, delivery record, or retrievable state MUST carry a webhook secret, a hash of it, or any value from which it can be checked; for secret generation and replacement, the audited prior and resulting state is the fact of the change and any declared overlap. Creating a webhook, and changing its filter, parameter derivation, or rate bound, MUST additionally be decided as an update that changes what its standing launch fires under RFC-0003:C-DECISION. A save whose parameter derivation names a parameter the standing launch's playbook does not declare, or whose expressions do not compile, MUST be refused. A webhook is enabled or disabled; enabling and disabling are update on the webhook. A disabled webhook, and a webhook whose standing launch is disabled, MUST NOT record deliveries. A recorded delivery that has not settled when its webhook is disabled or deleted MUST settle failed with that reason. The controller MAY disable a webhook after consecutive failed firings, counting only failures that do not derive from delivery content, and MUST audit that disabling with an actor that names the controller and no principal. Creating, enabling, disabling, and deleting a webhook, and replacing its secret, MUST be audited under RFC-0003:C-AUDIT.

Since: v0.1.0


Changelog

v0.1.0 (2026-09-13)

Initial draft

Added

  • C-ACT-AS, acting as a team narrows the subject to that team principal
  • C-WEBHOOK-DELIVERY, C-WEBHOOK-SETTLEMENT, C-WEBHOOK-LIFECYCLE, webhook triggers
  • C-ACTIONS: runs define approve, answering a decision request; C-APPROVALS grants it to the launch owner by default and never to a run principal

Changed

  • C-PRINCIPALS and C-TEAM-UX defer to C-ACT-AS
  • C-OWNERSHIP, C-HIERARCHY, C-DECISION, and C-ENFORCEMENT name webhooks and their deliveries
  • C-APPROVALS: a draft graduation binds the pack's digest, which for a pack saved on a base covers the flattened tree