/agents
Not yet servedList governed agents
Not yet available.
The HTTP surface serving three views: the developer creates a governed agent, the security owner reviews the policy diff, the operator works the agent card. No workflow or marketplace endpoint. The contract changes before the implementation does, never after.
The platform API is a private in-cluster service with no public hostname; a public endpoint will be published only once external reach is decided.
Every endpoint requires a bearer assertion in the Authorization header.
Short-lived, audience-bound identity assertion, verified by the API and never trusted from the browser. A human caller presents the console's assertion; a machine caller presents a Kubernetes projected service-account token (issuer = the cluster's service-account issuer, audience = necto-api, lifetime at most 600 seconds). The customer's identity provider proves who someone is; Necto alone decides what they may approve. Under federation the console assertion carries upstream_identity_path: ordered issuer, subject, verification_basis entries, origin first. Malformed paths are refused with 401. A machine caller is authorised by its committed client-principal record alone: an operation the record does not list is refused with 403, and a machine caller never approves a release.
Developer view: create a governed agent
List governed agents
Not yet available.
Compile a profile and return its artifacts
Not yet available.
Body: application/octet-stream
Open a pull request changing this agent's profile
Not yet available.
Body: application/json
necto.yaml content
Pull request opened
application/json → object
Compile a `kind: Task` and check that it narrows, never widens
A task compiles to a restriction set intersected with the target agent's already-approved compiled artifacts. Because a task widens no compiled artifact, a narrowed result needs no approval. If the intersection is not exactly what the task requested, compiling is refused with the offending field, never a coerced narrowing.
Body: application/octet-stream
The task did not parse, or it widened the agent's authority; either way the offending field is named. A widening refusal also carries requested and granted.
application/json → Problem
List scheduled tasks
Every kind: Task apply that carried a schedule. A one-shot task, one with no schedule, runs once and is never listed here, the same way nectoctl apply -f necto.yaml never lists a run.
A machine caller reads only the scheduled tasks of the one agent its operation is scoped to, whether or not it names that agent; naming another agent is refused. A human caller reads every scheduled task.
The agent whose scheduled tasks to list. Optional for a machine caller, whose own operation already scopes the answer; naming another agent is refused. A human caller reads every agent's tasks.
Security owner view: policy diff and approval
List releases awaiting review
Not yet available.
Compile a profile and open a release
Compiles, opens a release awaiting approval, and stops; it never changes a running agent. via: pull_request is the default and the required carrier in a customer production environment; via: direct records the profile in Necto's release store instead of a git commit, and is refused with 422 there. imageDigest is the prebuilt artifact the release binds -- an E2B template build id (a UUID), built outside this process -- and the governed run starts its sandbox from it. Optional only for a release that binds no artifact; a governed run refuses such a release rather than starting from a stage's default template. A template name, alias or tag is refused: it is movable, and a rebuild would change what runs with no new release. An OCI image digest is refused until an OCI path exists; only E2B template ids are accepted today.
A release for an agent the platform does not hold registers it from this profile: its diff is computed against a profile that grants nothing, so every source, tool, egress destination and authority change shows as added. A release for an agent the platform holds is diffed against the profile that agent's last release registered.
Body: application/json
necto.yaml content
pull_request, directThe prebuilt artifact the release binds: an E2B template build id (a UUID), never a template name, alias or tag, and never an OCI image digest until an OCI path exists.
via: direct refused because the context environment is production, the profile failed to compile, agentId differs from the profile's name, or agentId or profile is a count where an identity is required: a count is never an identity, and no release is created from one
application/json → Problem
The compiled policy diff
Computed on compiled artifacts, not on file text. A formatting-only change returns noPolicyChange: true with an empty changes array. The release's own approval window is approvalExpiresAt, and the recorded standing approval is rendered beside the diff with its own expiresAt when one is recorded and not revoked, so a client can warn before either lapses and renew it while the window admits a second approval.
Policy diff, with the release's approval window, the recorded standing approval when one is live, and the standing-approval proposal the approver would approve beside it. The proposal is the deployment's own resolution, never a request value; absent when nothing resolves, or replaced by the recorded revocation when the release's grant has been revoked.
application/json → PolicyDiff
Required-test results for this release
One entry per required test, with its stored artifact
application/json → array of RequiredTestResult
Approve or reject the compiled diff hash
The approver approves a hash, not a description. The request carries diffHash; if it no longer matches the release's current hash the approval is refused with 409. Refused with 409 when a required test failed, or when a citation no longer resolves.
Body: application/json
approve, rejectnot-needed, wrong-scope, test-failure, citation-gone, unsafe-change, otherA rejection's structured reason code, from the closed set. A rejection requires one; an approval carries neither a code nor a reason text. A code outside the set is refused 422 with field: reasonCode, and nothing is recorded.
Optional free text stored beside a rejection's code, shown beside the decision and escaped on render. It is never parsed for ids or commands, and a text over 200 characters is refused 422 with field: reason.
The standing Slack grant the approver gives with this approval: the channel id the diff read exposed, the caps and the expiry the approver confirms. channelId is an echo of the deployment's resolution -- the API refuses any other value -- and only an approve decision may carry it.
The caller authenticated and Necto grants them no approval authority. No identity-provider claim changes this. Distinct from 401, and never a read-only 200. A machine client never approves a release at all: its approval request is refused 403 even when its client-principal record lists this route.
application/json → Problem
Hash no longer current (approval void), required test failed, an unresolvable citation, a widening release approved by the customer without the Necto approval the two-party rule needs, or a standing approval that does not match the deployment's resolution: the echoed channel id is not the resolved one, the release's agent has no scheduled task, or the deliver alias resolves to no channel. title names which, because a caller cannot act on them the same way. A widening release needs both parties' distinct approvals on one hash: a customer's approval without the Necto approval is refused and nothing is recorded, while a Necto approval recorded before the customer's own is one of two and the release is not approved and not runnable until the customer's own distinct approval binds the same hash. A non-widening release is approvable by the customer's security owner alone.
application/json → Problem
Unapproved release expired after seven days. What would have been approved is no longer approvable.
application/json → Problem
The body is malformed: decision or diffHash is missing, diffHash is a count where a release identity is required, a rejection carries no reasonCode, an unknown reasonCode, a reason over 200 characters, or a code or text on an approval; standingApproval is malformed, names caps other than the standing approval's, or an expiry that is not a timezone-aware instant inside the standing approval's window, or rides a reject; or the release's own compiled profile does not compile write_external. field names which, and nothing is recorded.
application/json → Problem
Revoke the release's standing Slack approval
Any human holding approval authority for this agent may revoke, and the act is recorded. Revoking is never harder than granting, so the grant's original approver is not required. Every standing grant the release's current diff hash bounds is revoked, never only the latest one; the revocation is checked at the next scheduled mint and never interrupts a send already in flight (the named lag). The grants are not edited: the revocation stands beside them. A machine principal may never approve or revoke.
The caller authenticated and Necto grants them no approval authority, or the caller is a machine principal. A machine principal may never approve or revoke; no identity-provider claim changes either.
application/json → Problem
No release under this id, or no standing approval is recorded for the release's current diff hash.
application/json → Problem
Application customer plane: register the primary customer object and read it back
List the deployment's applications
Every application this deployment holds, in creation order. The tenant is the deployment's own; no request field selects it, and another customer's applications are never listed.
Register an application
The application is the primary customer object. One application keeps one stable identity across dev, stage and prod; this route registers that identity, and every environment, plan, deployment and record below keys on it. The body names the application and the source it is created from — one of the four accepted sources. The field set is closed: there is no field for code, a credential or persistent data, and an unexpected field is refused rather than ignored. The tenant is the deployment's own and never a request field; the creator is the assertion-resolved subject, never a body field.
Body: application/json
The application's display name
A field is missing or malformed, the source kind is not one of the four accepted sources, the OCI reference is not addressed by digest, or the body carries a field outside the closed set. field names it.
application/json → Problem
Read one application
One application by its stable identity, or 404 when this deployment holds none — including an application of another customer, which is not readable through this deployment's store.
Read the managed state one application environment owns
One logical database and role, bucket and queue per application environment, each with its own state and its own credential reference. Identifiers only — no field carries a credential value and no field carries payload content. The environment is the deployment's own selection; an environment the application holds no state for answers an empty list. Another customer's application is not one this deployment holds, and its read is a 404.
dev, stage, prodThe environment query parameter is missing, or names an environment outside dev, stage and prod. field names it.
application/json → Problem
Set up the managed state one application environment owns
The platform derives every name and reference; the request names only the environment. The logical database and role, bucket and queue names, and their credential references are the application-environment derivation's, never body fields. Setup is per service: while a provider does not answer, that service's state is recorded failed, never reported as success, and no credential is issued for it. A body field outside the closed set is refused rather than ignored. The tenant is the deployment's own; no request field selects it.
Body: application/json
A field is missing or malformed, the environment is not one of the three, or the body carries a field outside the closed set. field names it.
application/json → Problem
Record a secret's reference
The value is held by the secret manager; the platform records a reference. The body carries the secret's name and value exactly once; the answer carries the reference the platform derived from the tenant, the application, the environment and the name, and never the value. No record the platform writes holds a value. A blank name or value is refused rather than defaulted. The tenant is the deployment's own; no request field selects it.
Body: application/json
A field is missing or malformed, the environment is not one of the three, or the body carries a field outside the closed set. field names it.
application/json → Problem
Read the application's plan versions
Every plan version this application holds, oldest first. A version is immutable and identified by a content hash over the tenant key, the plan and its parent, so reading one back is reading what was recorded — never a later correction. provenance states, per plan field, whether the winning value came from detection or an explicit value. The tenant is the deployment's own and no request field selects it; another customer's application is not one this deployment holds, and its read is a 404.
The application's plan versions, oldest first
application/json → object
Detect a plan and record its version
Detection produces the visible, editable plan; the plan grants no authority. The application's registered source selects the construction path: a repository or template is detected through the adapter's closed input contract, a Dockerfile through the minimal read, and a prebuilt image skips detection. A key outside a closed contract, a language outside the fixture-tested set, and a detection that does not resolve to one plan are all refused rather than guessed; support is claimed per tested fixture only. saved is the saved project setting level; a field no level supplies is a refusal, never a default. The resolved plan is minted as an immutable version and answered with its per-field provenance. A detection resolving to the plan the application already holds answers with that version and records nothing new. The tenant is the deployment's own and no request field selects it.
Body: application/json
The plan the application already holds; identical content is the same version, so nothing new is recorded
application/json → ApplicationPlanVersion
A body field outside the closed set, a key outside the adapter's or the saved setting's closed contract, a language outside the fixture-tested set, a detection that does not resolve to one plan, or a Dockerfile the minimal read cannot resolve. field names it.
application/json → Problem
Correct the current plan version
A correction is a new immutable version, never a mutation of the one it corrects. The body's values is the closed necto.app.yaml schema — the correctable plan fields. A key outside it, toolchain and source included, is refused as unknown, and customer_id is refused like any other unknown key because the deployment supplies the tenant key, never a request. Every field the correction does not set keeps the value and the provenance it had; every field it sets carries the entered level. The corrected plan must differ from the current version: identical content is the same version, refused rather than recorded as a second one. The corrected version's parent is the current version, so a correction never forks the lineage. An application with no recorded plan has nothing to correct and answers 404.
Body: application/json
No such application for this deployment, or the application holds no plan version to correct
application/json → Problem
values is missing or not the closed necto.app.yaml schema, a key outside it is refused as unknown, or the correction changes nothing — identical content is the same version, not a new one. field names it.
application/json → Problem
Read the application's graph and default guardrails
The read-only graph and the default guardrails, resolved for one application's accepted plan version. Every field carries the source it came from and the accepted plan version it was read at. The plan is topology only: a graph field is never compiled into a guardrail, so a tool, source, destination or credential appears only where the governed profile declares it. The placement is computed from the accepted plan's requested tier and the governed profile, never customer-forced. A refusal — a conflicting placement or a governed profile that does not compile — is visible in refusals with the policy decision's own reason and safe metadata, never a generic application error. The tenant is the deployment's own; another customer's application is a 404.
The graph, the default guardrails and any visible refusal
application/json → ApplicationGuardrails
No such application for this deployment, or the application holds no accepted plan version.
application/json → Problem
Deploy the accepted plan to dev
A dev deployment gets an automatic record; no approval. The accepted plan version is named by its content hash and reconciled into the application environment's resources: standard components become restricted Kubernetes resources and governed components runtime start requests. Placement is computed by the platform — the namespace from the tenant, application and environment, and the tier from the accepted plan and governed profile — and neither is read from a body field. The governed profile hash and the compiled-artifact hashes on the record are the platform's own derived values. A deployment whose governed profile or compiled artifacts differ from the environment head record's is refused without an approval; the deployment-approval path is not built yet. Nothing reaches a cluster here; the live adapters are a later ticket.
Body: application/json
The namespace derivation or the tier classification seam is not wired. Refused fail-closed; never defaulted.
The state store is busy when another writer holds its write lock past the store's own wait: the request is answered 503 with a Retry-After header before the route acts. The store releases its own lock before refusing, so later operations proceed.
application/json → Problem
The deployment changes the governed profile or the compiled artifacts the environment head record carries, and no approval is bound to the change. The deployment-approval path is not built yet, so the deploy refuses rather than landing unapproved.
application/json → Problem
A body field outside the closed set, a missing or malformed image digest, an accepted plan hash the application does not hold, a governed component with no registered governed profile, or a placement the platform refuses. field names it.
application/json → Problem
Application customer plane: promote one application through dev, stage and prod
Open a promotion request to the next environment
One hop at a time, dev to stage to prod; skipping a stage is a refusal, never a warning. The request carries only an image digest, an accepted plan version and identifiers — there is no field that could carry a credential or persistent data, and none is accepted. The governed profile and compiled-artifact hashes the approvers sign are the platform's own derivation from the application's registered governed profile, never request fields. The tenant is the deployment's own; no request field selects it. The target environment's namespace is derived by the platform from (customer, application, environment) and never read from this body; the runtime tier is computed from the requested tier and the governed profile, never posted. Both seams refuse fail-closed with 503 while unwired.
Body: application/json
dev, stage, proddev, stage, prodsha256:<hex>; an OCI image addressed by digest, never a tag
standard, governedthe accepted plan's requested tier; the platform computes placement
Test-name and artifact-URL identifier pairs — the record links tests, never their content
A field is missing or malformed, the environments name a hop that is not the next one, the image is not addressed by digest, or the requested tier is unknown. field names it.
application/json → Problem
The namespace derivation or the tier classification seam is not wired. Refused fail-closed; never defaulted.
The state store is busy when another writer holds its write lock past the store's own wait: the request is answered 503 with a Retry-After header before the route acts. The store releases its own lock before refusing, so later operations proceed.
application/json → Problem
Approve or reject a promotion, bound to the combined hash
The approver approves a hash, not a description. Two distinct, assertion-resolved approvers are required before a promotion may execute. The requester cannot approve their own promotion; a subject has one decision per promotion; an approval naming anything but the promotion's current combined hash is refused. The approval records the target environment's head deployment record at decision time, and expires with the deployment's approval window.
Body: application/json
approve, rejectThe caller authenticated and Necto grants them no approval authority. No identity-provider claim changes this. A machine principal never approves a promotion either: its request is refused 403 even when its client-principal record lists this route.
application/json → Problem
A named refusal: self-approval refused, approver decided twice, or promotion hash mismatch — title names which, because a caller cannot act on all of them the same way.
application/json → Problem
Execute an approved promotion
Executes only when two distinct assertion-resolved approvals bind the promotion's combined hash, neither has expired, the target environment's head deployment record is still the one the approvals recorded, and a private-e2b target's placement gate is proven. The platform recomputes the governed profile hash and the compiled-artifact hashes from current state: when they differ from what the approvals signed, the approval is void and nothing is recorded as deployed. The new deployment record lands through a compare-and-swap on the target head: two concurrent executes succeed exactly once. No override parameter exists.
A named refusal: promotion rejected, insufficient approvers, approval expired, approval void (the governed profile or compiled artifacts changed after approval), target changed (head moved, or the compare-and-swap lost), or gate unproven for a private-e2b target. title names which.
application/json → Problem
Application customer plane: the tenant's private collaboration journal and version history
The application's collaboration mutation journal, newest first
Every mutation records the actor and the resulting version. The append-only journal is read one application at a time, newest first, tie-broken by insertion order the way the durable backend's rowid does. Every field is an identifier — subject, object id, version — never a comment body, prompt or payload. The deployment's own tenant key is carried on the record and is never selected by the request. A caller whose assertion verified and for whom Necto holds no authority record is refused 403; a machine principal is refused by its own client-principal record.
The application's mutation journal, newest first
application/json → object
No application is registered in this deployment under this id. A cross-tenant application is not registered here either: the registry is tenant-keyed and this read never crosses it.
application/json → Problem
The application's version history, derived at read time
Read-only by shape. The history is derived at query time from the stores that already own the rows — the accepted plan versions, the deployment records, the release records and the run registry — and writes nothing. Every entry carries the anchor kind, the identifier its store knows it by, the time and the actor where the store has them. A plan version has no clock and no actor on the artifact; a run record has no actor; the entry reports the absence rather than inventing one. A plan version's parent is carried by hash. The plan store has no application key, so the tenant's plan versions are part of every application read; a version is not attributed to one application by a key that does not exist. The deployment store's only list-shaped read is the record in force per environment, so the history lists the current head and not every superseded record. The tenant is the deployment's own and is never taken from the request.
The application's version history, newest first
application/json → object
No application is registered in this deployment under this id. A cross-tenant application is not registered here either: the registry is tenant-keyed and this read never crosses it.
application/json → Problem
Grant a collaboration role on an application
The first administrative surface. The caller resolves in Necto's authority store — the operator is the first administrator — and the granted subject is named by its issuer and subject, never by an email address. The grant is per application; the store is bound to the deployment's own tenant key, so no request, claim or header selects another. Every grant appends a role_grant record to the mutation journal before the store moves. An approving grant alone does not approve: approval still reads the authority record.
Body: application/json
The issuer half of the granted subject's key
The stable subject identifier; an email address is refused
viewing, editing, deploying, approving, stoppingNo application is registered in this deployment under this id. A cross-tenant application is not registered here either: the registry is tenant-keyed and this route never crosses it.
application/json → Problem
The subject already holds a grant on this application. A role change is a revocation and a new grant, never an edit.
application/json → Problem
The body is malformed or carries a field the closed set does not name; issuer, subject or role is missing or blank; the role is not one of the five; or the subject looks like an email address. field names which, and nothing is recorded. Never a 500.
application/json → Problem
Revoke a collaboration role on an application
Revoking is never harder than granting. The caller resolves in Necto's authority store; the grant is removed, never edited, and the revocation appends a role_grant record to the mutation journal. A revocation with no grant in force is refused 409: every recorded revocation is attached to the version it removed.
The stable subject identifier of the grant to revoke
The issuer half of the grant's key; a subject is only unique within its issuer
No application is registered in this deployment under this id. A cross-tenant application is not registered here either.
application/json → Problem
The subject holds no grant on this application to revoke. A no-op revocation is refused, never filed.
application/json → Problem
The issuer query parameter is missing or blank, or the path's subject looks like an email address. field names which, and nothing is recorded. Never a 500.
application/json → Problem
Clone an application's plan version into a new application
A clone is a new application carrying the source plan version's topology only. The clone's new plan version is minted field by field from the source plan's ten topology fields, with the source version hash as its parent; the source version, application and repository are unchanged. No approval, deployment, release, run, role grant, credential, secret or managed-state resource is carried: none has a plan field or a parameter to travel through, and each environment of the clone resolves its own secrets. One plan_version cloned record is appended to the mutation journal, naming the actor, the source version hash and the clone's version hash; a record the journal refuses leaves no clone. The source version is read through this deployment's own tenant-keyed plan registry, so another customer's version is not found — refused 404, never cloned. The caller needs an editing collaboration grant on the source application; without one the clone is refused 403. The field set is closed: a field outside it is refused rather than ignored, and a blank or malformed field is refused 422, never a 500.
Body: application/json
The source plan version hash (sha256:<hex>); the clone's new version is parented to it
The clone's new stable application id; the clone's plan version is keyed by it and no request field selects the tenant
The clone's new plan version recorded; the source application and its version are unchanged
application/json → ApplicationClone
The caller resolves in Necto's authority store but holds no editing grant on the source application, or the source version is another customer's. Nothing is cloned either way.
application/json → Problem
No application is registered in this deployment under this id, or the application holds no plan version with this hash. A cross-tenant source version is not one this deployment's registry holds either.
application/json → Problem
The clone's new application id is already registered. A clone mints a new identity, never overwrites one.
application/json → Problem
The body is malformed or not valid UTF-8, or it carries a field the closed set does not name; sourceVersion or newApplicationId is missing, blank or not a string. field names it, and nothing is cloned. Never a 500.
application/json → Problem
List one application's comments, newest first
One row per comment, and a redacted comment carries no body. Each item is the newest row of one comment: the identifier, the application, the anchor, the actor and the time of that newest row, whether it is a redaction, and how many revisions the store holds. A live comment carries the newest body; a redacted comment carries no body at all, because a redaction hides the body rather than overwriting it. The optional anchorKind and anchorId filters narrow the list to one anchor, and both are needed. The read needs the caller's viewing grant on this application: no grant is 403, and the store is bound to the deployment's tenant key, so another customer's comments are never listed. An unknown anchor kind is refused 422 rather than answered with a whole application's comments.
plan_version, deployment, run, policy_versionThe anchor kind to filter by; anchorId is needed beside it.
The anchor's own identifier to filter by.
One application's comments, newest first
application/json → object
No application is registered in this deployment under this id. A cross-tenant application is not registered here either.
application/json → Problem
An anchor filter is malformed: anchorKind is not one of the four anchor kinds, anchorKind is given without anchorId, or anchorId is blank or given without anchorKind. field names which, and nothing is read. Never a 500.
application/json → Problem
Record a comment on an application
The body is user content and enters no trail. The caller needs the editing grant on this application: a viewing grant alone is refused 403, and no grant is refused 403 before anything is recorded. The actor is the caller's resolved subject key and the time is the platform clock, never request fields. The comment store appends the row and writes the one comment journal record; the route writes no second record. The response carries the recorded comment's identifiers and never the body: the body is read back through the list route, and no comment body enters the journal or the evidence timeline. A comment id the store already holds is refused 409: an edit is a revision, never a second creation.
Body: application/json
The comment's own identifier, stable across its revisions.
The comment content. A blank body is refused 422, and a non-UTF-8 body is refused 422 and never reaches the store.
Recorded in the comment store and the mutation journal; the comment's identifiers and revision count. The body is not echoed.
application/json → Comment
No application is registered in this deployment under this id. A cross-tenant application is not registered here either.
application/json → Problem
The comment id is already recorded in this store. An edit is a revision of the same comment id, never a second creation.
application/json → Problem
The body is malformed or carries a field the closed set does not name; commentId, anchor or body is missing, blank or not a string; the anchor kind is not one of the four; or the body is not UTF-8. field names which, and nothing is recorded. Never a 500.
application/json → Problem
Redact a comment
A redaction appends a revision; it never mutates or deletes one. The caller needs the editing grant on this application: a viewing grant is refused 403, and no grant is refused 403 before the comment is read. The actor is the caller's resolved subject key and the time is the platform clock, never request fields. The store appends one redaction row carrying the version of the revision it hides; the hidden revision stays in the store and the journal keeps both versions, never overwritten and never deleted. The response carries the new row's identifiers — including the redacted revision — and never a body: a redacted body enters neither the journal nor the evidence timeline. A comment this deployment does not hold is 404, and a comment already redacted is 409: a redaction is final, and a later revision cannot put the body back. A comment of another application in this tenant is not this application's: the editing grant is per application, so the redaction never reaches it.
The comment's own identifier, stable across its revisions.
Recorded in the comment store and the mutation journal: the redaction's identifiers, the redacted revision id and the revision count. No body is echoed.
application/json → Comment
No application is registered in this deployment under this id, or the application holds no comment under this comment id. A cross-tenant comment is not held here either, and a comment of another application in this tenant is not this application's either: the editing grant is per application.
application/json → Problem
The comment is already redacted. A redaction is final and a second one is refused, never filed.
application/json → Problem
The store refused the redaction rather than filing it: the platform clock is not after the comment's newest revision, so the append would break the append-only order. field names the comment, and nothing is recorded. Never a 500.
application/json → Problem
Operator view: agent card, evidence, stop, revoke
The application's run budget for its current window
The per-application run budget: the cap on runs in one fixed window, the window's own count and the runs remaining. The claim that enforces it happens at run admission, in the same step as the run's own registration, so a start past the cap refuses before any sandbox starts and records no run. Runs are the counted dimension; a spend dimension joins when the metered instrument records it. No budget recorded is a 404: the read answers the record and never a default cap. The tenant is the deployment's own; no request field selects it.
The agent catalogue card
Denied requests are returned alongside successful runs, not behind a filter.
What is usual for this agent, by release regime
The baseline a run is compared against: the documents, destinations, tool order, records-per-run and run-seconds the agent's release regimes have actually shown. A baseline is keyed to one agent, one compiled profile and one release, so a release opens a new regime instead of being reported as drift on release day; a prior regime's baseline is kept for comparison and never scores the next one. transitions names where one regime's baseline ended and the next began.
The four dimensions are facts, never a score. state is ready when the regime has enough observed history to compare against, and insufficient_volume when it does not: the state scores nothing, and the comparison carries no verdict rather than a low one. observedRuns is the number of the regime's finished runs the baseline summarises. Identifiers only: document ids, destination names and tool names, never content, passages, prompts or credentials.
Start a governed run
Returns identifiers only: no document text, no raw prompts, no credential values. The run is asynchronous; watch /runs/{runId}/timeline?watch=1 for the ordered TimelineEntry stream.
A registered machine client may start the run from a schedule tick by sending a trigger. The run then takes its input from the scheduled task the trigger names, records the tick on the run's timeline, and carries the trigger on the card's run summary. A caller that is a person, a trigger that names no task of this agent, an instant that is not a tick of the task's schedule, or a tick outside its own period is refused, and nothing starts. A tick is outside its own period when it is more than one minute after the current instant, or more than one period of the task's own schedule plus a short transport margin before it: the tick is stale once the next tick exists. A tick refused as stale is recorded as a skipped tick on the agent's schedule stream, while a tick refused before its period is due is recorded on that stream under the distinct kind schedule_refused, without a tick key, so the refusal does not pre-decide it. A repeated trigger key starts no second sandbox: the response carries the run the key already names.
Body: application/json
The caller's message for this run. Required for a run with no trigger; a scheduled run takes its input from the scheduled task the trigger names.
The trigger key already named a run, so no second sandbox starts. The response carries the existing run id and replayed: true.
application/json → object
Run accepted. The sandbox is stopped as soon as the entrypoint returns, and the stop is recorded on the run's timeline: sandbox_killed when it stopped, or sandbox_stop_failed when it did not, so a sandbox left running is never reported as stopped. The substrate's inactivity timeout stays only as the backstop for a crashed coordinator path, and the run's own exit result is unchanged by a failed stop. The moment the entrypoint returns, the run's timeline gains run_finished with the exit code and the stdout/stderr byte counts, never the output.
application/json → object
The prebuilt artifact the sandbox ran from: an E2B template build id (a UUID). Present only for a governed run; the in-process reference workload starts no sandbox and names no artifact.
The substrate's own sandbox identifier. Present only for a governed run.
The artifact entrypoint's exit result. Present only for a governed run; 0 ran, and the entrypoint's own non-zero code is recorded unchanged.
The agent is contained, a request arrives while the agent already has a run that has not finished, the tick is outside its own period, the tick is already recorded as skipped, or the previous run's sandbox did not stop: it stays registered as live and this run starts no second sandbox, so no agent ever has two live sandboxes. One agent has one live run: a second start, triggered or not, starts no second sandbox, and a triggered one is recorded as a skipped tick with reason overlap. An agent with no approved release starts nothing: a release whose approval is rejected, void or expired, and a widening release whose second distinct approval has not arrived, are not approved releases, so the run is refused before a sandbox exists. Stopping an agent prevents new runs as well as killing the running one; a stop that let the next run start would be a pause. Returning to service is a release, not this call succeeding on a retry. A stop that lands while a run is starting kills the sandbox that started, and the kill is recorded on that run's timeline, so no sandbox is left running after a stop. A tick that is refused is recorded as a skipped tick on the agent's schedule stream, with reason contained, overlap or refused: a run the release gate refuses is an authority refusal, and so is a tick outside its own period. A tick refused by a gate is never also a run, and a repeat of its key answers the skip rather than a run. A tick outside its own period is audited on that stream; only one whose period is over is recorded with its tick key. A tick already recorded as skipped is never caught up: the refusal names the reason it was recorded with.
application/json → Problem
The caller may not start a triggered run: a trigger comes from a registered machine client, and a person's trigger is refused.
application/json → Problem
The request names neither a trigger nor an input, or the trigger is malformed: its key does not match the task id and the scheduled instant, or it names no task of this agent, or the instant is not a tick of that task's schedule. A malformed trigger names no tick the scheduler could have observed, so nothing is recorded as a skipped tick. A triggered run the governed path then refuses without authority — a profile that does not compile — is recorded as a skipped tick with reason failed.
The application's run budget is exhausted: the claim past the cap refuses before the run is registered and starts no sandbox, and a triggered tick is recorded as a skipped tick with reason refused.
application/json → Problem
The substrate is unavailable: no run entrypoint is configured, the sandbox did not start, or it did not execute the run. A triggered tick refused before its run is recorded — no run entrypoint — is recorded as a skipped tick on the agent's schedule stream, with reason failed: the substrate failing is not the authority refusing. A triggered tick whose run record exists and whose sandbox then fails is a failed run: its key names that run, the failure is recorded on the run's own timeline (sandbox_start_failed when the sandbox did not start, sandbox_exec_failed and the kill when it did not execute the run), no skipped tick is written beside it, and a repeat of its key answers the recorded run.
The state store is busy when another writer holds its write lock past the store's own wait: the run route answers 503 with a Retry-After header and starts no sandbox. The store releases its own lock before refusing, so later operations proceed.
application/json → Problem
The agent's schedule stream, newest first
Every tick this agent's scheduler recorded, newest first: the accepted ticks beside the ticks that did not run. An accepted tick is the run's own schedule_fired entry; a stale tick is the keyed schedule_skipped with reason refused; a tick refused before it could have run is the distinct schedule_refused with reason future and no key; the remaining skips carry contained, overlap, failed or missed. Each entry carries its tick key where one exists, its task id and its scheduled instant beside the instant the platform recorded it. A human caller with authority for the agent reads the stream; a machine client and a workload-originated call are refused, because the scheduler reports ticks and never reads them back. An unknown agent is refused like the card. limit defaults to 50 and a value outside 1 to 200 is refused by name rather than clamped.
How many of the newest entries to return. Default 50; a value outside 1 to 200 is refused rather than clamped.
The agent's schedule stream, newest first: the accepted ticks and the ticks that did not run, each with its timestamps; the ticks that did not run carry their reason.
application/json → object
The caller authenticated and Necto holds no authority record for the subject, or the caller is a machine client or a workload-originated call. The schedule stream is read by a human caller with authority for the agent; a machine client reports ticks and never reads them back.
application/json → Problem
The limit is not an integer, or it is outside 1 to 200. The refusal names the field rather than clamping the value, so a caller never reads a shorter answer as the one it asked for.
application/json → Problem
Record a tick the scheduler observed as missed
Machine-only. A scheduler reports a tick it did not fire because it was past its grace window. The API recomputes the trigger key from the task id and the scheduled instant and records a skipped tick with reason missed on the agent's schedule stream. A key that already names a run or a skipped tick is a no-op: the scheduler's boot look-back reports every past-grace tick it can see, and the API is what de-duplicates. A caller that is a person, or a request that does not name a missed tick of this agent, is refused.
Body: application/json
The skipped tick is recorded, or the key already named a run or a skipped tick and nothing new is written.
application/json → object
The caller may not report a missed tick: schedule events come from a registered machine client, and a person's report is refused.
application/json → Problem
The report is malformed: its key does not match the task id and the scheduled instant, or it names no task of this agent, or the instant is not a tick of that task's schedule.
application/json → Problem
Mint and execute one standing-approved send
The scheduled-run send surface. A scheduled run mints one fresh, ordinary, single-use ActionEnvelope under the standing approval it names and executes it through the existing single-use consumption path: no second consumption model, no reusable envelope, and no adapter change. The grant approves one channel and one message schema; the envelope names the grant in approvalRef and the grant's approver in caller, so the adapter's existing per-call revalidation is the check the send passes. Every refusal the standing-approval rules define is this route's own refusal, never a 500: an unknown grant is 404; a revoked grant, a channel outside the grant, a URL host the profile's allowlist does not admit and a tool the compiled allowlist does not permit are 403; a release change that voided the grant is 409; an expired grant is 410; a payload outside the approved schema, a field over its length cap, a title or price carrying URL-like text, or a url that is not exactly one absolute https URL is 422; and a mint over the per-run or per-day cap is 429. A workload-originated call is authorised by its own run. The console's workload relay verifies a DPoP-bound workload token and mints the assertion with the verified workload claims beside the sandbox instance id as sub: agent_id, run_id, tool and audience. A call carrying those claims is refused on every route but this one, and on this route it is refused unless that run exists, is live, was started by Necto for this agent, runs in that instance, the tool is one the run delivered, the instance's workload tokens have not been revoked by a stop, this request's runId and tool are the assertion's, and the forwarded audience is the verified tool's own per-tool audience necto:<agentId>:<tool>. The revocation is read from the deployment's own revocation list by the instance the assertion names; a deployment with no such list to read, or a list that cannot be read, refuses the call rather than assuming the instance is not revoked. Only the run's own workload call may spend the standing approval. A session assertion is refused here with 403, whatever authority it holds: a human approves each send through the ordinary approval surface. The per-run cap is counted on the run the relay verified, never on a value in this request, so a request naming another run is refused and cannot reset it.
Body: application/json
The standing approval the send is minted under
The scheduled run this mint is for. The per-run cap is counted on the run the relay verified, never on this value; a request naming another run is refused and cannot reset it.
The channel the run intends to post to. The grant is scoped to exactly one channel id; anything else is refused.
The tool the run invokes. The relay's verified tool claim must name exactly this tool, and the tool is revalidated against the agent's compiled action allowlist at execution time.
The one message schema the grant approves, exactly: an extra or missing field is refused, never dropped or supplied. Every field is bounded: a longer value is refused before any pattern runs. The URL must be exactly one absolute https URL -- no whitespace, no second URL and no userinfo -- whose host the profile's compiled egress allowlist admits. The title and price are plain text: URL-like text in either -- a scheme, www., a domain whose final label the IANA root zone delegates, or an obfuscated spelling of one -- is refused, so a URL cannot ride a field the host check does not read.
Two limits, told apart by the body. The surface's rate limit refused the request before the route ran: rate limited with Retry-After. The standing approval is at its per-run or per-day cap: the title names the cap.
application/json → Problem
The send was executed once, by identifier
application/json → object
Derived from the envelope hash, never minted at execution; the ledger consumes it exactly once.
Slack's own identifier for the message it created. The message text is never returned or recorded.
No such agent, or no standing approval is recorded under the named grant id
application/json → Problem
The agent is contained, the grant is void because the release changed after approval, or the agent has no approved release to bind the grant to
application/json → Problem
The deployment has not opted in to the standing approval, the caller is not the run's own workload call, the request's run is not the verified run, the forwarded audience is not the verified tool's, the instance's workload tokens were revoked by a stop, no workload-token revocation list is composed to read, that list could not be read, the grant was revoked, the channel is outside its scope, the URL host is not one the profile's allowlist admits, or the compiled allowlist does not permit the tool
application/json → Problem
A required field is missing, the payload is not the approved message schema, a field is over its length cap, the title or price carries URL-like text, the url is not exactly one absolute https URL, or the profile did not compile
application/json → Problem
Slack refused the send or could not be reached. The detail carries Slack's own error code, never the message text.
application/json → Problem
The deployment composes no Slack transport, or its Slack credential is not configured.
The state store is busy when another writer holds its write lock past the store's own wait: a write that precedes the action it records -- the credential-novelty entry, the identity assertion or the authority journal -- fails closed, so the request is answered 503 with a Retry-After header before the route acts. The store releases its own lock before refusing, so later operations proceed. The per-request audit record is written after the outcome exists: its write is retried a bounded number of times, and if the store stays busy the response stands and one structured warning names the gap.
application/json → Problem
Record one destination a governed run's egress refused
The relayed refusal report. A newly onboarded agent starts with zero egress permissions, so every destination it tries is refused; the sandbox reports the refused destination's identifiers and the platform records them for the customer's review. Identifiers only: the host, the port and the protocol, the run's governed-component identity and the run, the platform's own instant and the bounded attempt counter; no payload, content or capture field exists. A workload-originated call is authorised by its own run. The console's workload relay verifies a DPoP-bound workload token and mints the assertion with the verified workload claims beside the sandbox instance id as sub. A call carrying those claims is refused on every route but the relayed routes, and on this route it is refused unless that run exists, is live, was started by Necto for the path's agent, runs in that instance, the instance's workload tokens have not been revoked by a stop, and the tool is one the run delivered. The revocation is read from the deployment's own revocation list by the instance the assertion names; a deployment with no such list to read, or a list that cannot be read, refuses the call rather than assuming the instance is not revoked. The body's field set is closed: host, port and protocol only, and an unexpected key is refused. The tenant key, the run's governed-component identity and the run come from the deployment and the verified claims, never from this body: the body carries no runId, so a caller cannot file a refusal under another run. A repeat observation coalesces into its row's counter up to the platform's cap, and a new destination past the run's cap is not recorded: the answer is 202 with recorded: false and reason: flood-cap, and the run is not failed. The cap value is set by the change that lands the row, never by this document.
Body: application/json
The named host the run's egress refused. A wildcard, a CIDR, a scheme, a port or a path is refused; the host is normalized before it is stored.
The TCP port the run tried
tcp, tlsThe connection's closed protocol set
The refused destination was recorded; a repeat observation answers the same way with its counter coalesced
application/json → ObservedDestination
The run already recorded the platform's cap of distinct destinations; this new one is not recorded and the run is not failed
application/json → object
Falseflood-capA required field is missing, the host is not a named host, the port is outside 1-65535, the protocol is not tcp or tls, or the body carries a field the report does not declare
application/json → Problem
One run, end to end
Identifiers and decisions only: no document text, no raw prompts. Dropped candidates are included with their reason; a timeline showing only what was retrieved hides the control that was exercised. watch=1 upgrades the response to a Server-Sent Events stream, one TimelineEntry per event; omit it for the current snapshot. A workload-originated read is authorised by its own run: the console's workload relay mints the assertion with the verified workload claims beside the sandbox instance id as sub, and a call carrying those claims is refused on every route but this one and the actions route. The read is refused unless that run exists, is live, was started by Necto for the assertion's agent, runs in that instance, the instance's workload tokens have not been revoked by a stop, and the tool is one the run delivered. The revocation is read from the deployment's own revocation list by the instance the assertion names; a deployment with no such list to read, or a list that cannot be read, refuses the read rather than assuming the instance is not revoked. A session assertion is decided exactly as before. Every entry carries its own evidence class: semantic for a kind the four behavioural dimensions read, infrastructure for every other kind. An entry presented as semantic agent evidence is checked by require_semantic before it is presented, and a refusal is the named 409 whose detail names the kind.
Stream new entries as Server-Sent Events instead of returning a snapshot
Immutable timeline entries, or an SSE stream when watch=1
application/json → array of TimelineEntry
text/event-stream → string
An entry presented as semantic agent evidence that the partition does not read as semantic: the refusal names the kind in detail, because a caller has to know what was handed over. A word and the kind, never a number and never a semantic claim.
application/json → Problem
One run's chat stream, as AG-UI events
The small optional chat surface, behind a Necto-owned interface: AG-UI's event names on Server-Sent Events, so the protocol adapter may be replaced without changing the application or the evidence models. Every event is derived from the run registry and the run's own timeline: run state is the registry's own rows, and tool activity is the run's own credential-injection entries. Identifiers and decisions only: no message text, no tool arguments and no raw chain-of-thought is emitted or stored, because the platform stores none. The caller is authorised by the same record every run read uses: a caller Necto holds no authority record for is 403, and a run Necto never registered is 404 rather than an empty stream.
One Server-Sent Events data: frame per event, each frame a JSON-encoded event whose type is an event name this interface emits.
text/event-stream → string
One run's behaviour against its regime's baseline
The same four dimensions for one run — documents read, destinations contacted, tools and their order, records opened and seconds elapsed — plus the comparison against the baseline of the regime that run belongs to, as it stood before the run started. Only that regime's runs that finished before this run started count, so a later or unfinished run never widens the baseline an earlier run is compared against. A run is never compared with another regime's baseline or with itself, and the response names the regime it was compared against.
Refused attempts are observations, never absences: an attempt the platform refused is returned with the rule that refused it. state is ready when the regime has enough observed history to compare against, and insufficient_volume when it does not: the state scores nothing, and the comparison carries no verdict rather than a low one. Identifiers only: document ids, destination names and tool names, never content, passages, prompts or credentials.
Whether the calling workload instance's tokens are revoked
The console workload relay's own read, before it forwards a relayed request. The instance is the one the verified console workload assertion names as its subject; the tenant is the deployment's own and is never taken from a claim or a request value. The read takes no path or query parameter, performs no action and reads no run: it answers for a stopped instance too, so a revoked presentation is refused however fresh its proof is. Answers {"revoked": false} while the instance's tokens are live, and {"revoked": true} once a stop has revoked them. A caller that is not a workload assertion is refused: a human session and a machine principal are 403. A deployment whose revocation list is absent or unreadable is answered 503, so the relay forwards nothing rather than a presentation nobody checked.
Whether the instance's workload tokens are revoked
application/json → object
True once a stop revoked this instance's outstanding workload tokens. The read answers this for a stopped instance as well.
The caller is not a workload assertion: a human session or a machine principal.
application/json → Problem
The deployment holds no readable workload-token revocation list, so no instance can be answered about.
application/json → Problem
Release a previous version of this agent
Not yet available.
Body: application/json
A prior currentVersion from this agent's card
Whether this agent may use a tool, without invoking it
Answers nectoctl auth can-i. Evaluates the agent's compiled action allowlist for the calling identity; never invokes the tool and never mutates anything.
Stop the agent immediately
Not a release. No pull request, no approval, no build in this path. Safe to call on an already-stopped agent; still recorded.
Stop accepted and recorded. observedSeconds is how long this stop took, measured, not a promised ceiling. No bound is stated until one has been measured for the selected runtime and connector.
application/json → object
Revoke the agent's credentials upstream
Invalidates upstream, verified against the upstream rather than against Vault's own view of its lease. Distinct from stop: nothing revokes a Vault lease when a workload dies.
Revoke accepted and recorded. Each entry in revocations states whether the upstream supports revocation at all and whether an upstream call made afterwards actually failed. A connector whose credential is not Necto's to invalidate reports supported: false rather than a success nobody can verify.
application/json → object
Export a review bundle for a period
The export is itself an action, recorded in the timeline. The bundle carries identifiers and decisions, never content. Assembled in the request, from what the evidence store holds, so it returns with the identifier. complete is false when the period predates the retention cutoff: older entries may be purged.
Body: application/json
from or to is missing, is not an ISO-8601 instant, carries no offset, or the period ends before it starts
application/json → Problem
The caller's own identity and approval authority
Derived from the audience-bound assertion, never from the browser. Answers nectoctl auth whoami, and is distinct from /agents/{agentId}/authority/check, which answers for an agent rather than for the caller.
The assertion verified, and Necto holds no authority record for this subject. Distinct from 401: the caller is who they say they are, and that is not by itself authority. Deny by default, never a read-only view of someone else's.
application/json → Problem
This deployment's federation registry records
Console service identity. The console server reads the committed federation registry live over its existing east-west path and keeps no copy of its own: the upstream issuers, the named endpoint hosts and the allowed verification bases this deployment's records hold, so a registry record and a console provider config cannot disagree silently. The read is the console server's own call, never a signed-in person's: an assertion that is not the console's service identity (iss = the deployment's console issuer, sub = necto-console) is refused. Tenant- and deployment-scoped: the records are the deployment's customer id's, read from the deployment and never from a request, claim or header. Identifiers and display fields only, never a secret: no client credential, domain or endpoint path is in the record or the answer. limit defaults to 200 and a value outside 1 to 500 is refused by name rather than clamped.
How many records to return. Default 200; a value outside 1 to 500 is refused rather than clamped.
This deployment has no federation registry composed, so the route refuses rather than answering an empty list. A missing or malformed committed source refuses the boot through the loader instead.
application/json → Problem
This deployment's federation registry records, by issuer
application/json → array of FederationProvider
The assertion verified and the caller is not the console server's own service identity: a signed-in person, a machine client and a workload-originated call are refused. The registry read is the console's, not a user's.
application/json → Problem
limit is not an integer, or it is outside 1 to 500. The refusal names the field rather than clamping the value.
application/json → Problem
Every object the API returns or accepts, with its fixed field set.
The offending profile field, for a 422
What the caller asked for, on a kind: Task widening refusal from POST /tasks/compile: the single source, tool or authority value named by field.
What the target already grants, alongside requested: the profile's own authority for a widened authority, or its comma-joined data/tools list for a source or tool the profile does not list.
read_only, draft_only, write_reversible, write_externalThe compiled artifacts, each content-addressed, plus the containment plan added on acceptance.
Five questions for this agent: who may trigger containment, what triggers it, what changes immediately, what evidence remains, who can restore service. Declaring a plan is not evidence that containment works.
awaiting_approval, approved, rejected, void, expiredUnapproved releases expire after seven days
pull_request, directHow the profile text was recorded. direct is refused when the calling context's environment is production.
The prebuilt artifact the release binds. Present only for a release that binds one; absent reads as absent rather than as a blank a reviewer might take for a value.
True for a formatting-only change
The release's own approval window: the instant the release stops being approvable, computed by the platform from the recorded release, never by the caller. Present on every diff read, after the window passed included.
The standing approval the release has recorded, beside the diff: the grant the next scheduled run mints under when one is spendable, otherwise the release's latest recorded grant, whose expiry has passed. Present when a grant is recorded and not revoked; absent when the release has no grant, and replaced by standingApprovalRevoked when the recorded grant was revoked. A grant past its own expiry is still rendered, because the expiry is the fact a client warns on and the approval route's own refusal is the enforcement.
The grant the next scheduled run mints under when one is spendable, otherwise the release's latest recorded grant.
The one channel the grant is scoped to
The human whose approval the grant records
The instant the grant was recorded
The instant the grant stops being usable. A second approval before the release's own window passes records a fresh grant; after it passes, renewal is a new release.
What the approver would approve beside the diff: the release's agent's scheduled task deliver alias resolved to one Slack channel id by the deployment's composition, never a request value. Absent when the deployment registers no channel for the alias, the agent has no scheduled task, or the release's profile does not compile write_external.
The one channel id the deployment resolved for the deliver alias. The approver echoes it back; the API refuses a value that is not this one.
The one message schema the grant approves
The per-run cap, never a request value
The per-day cap, never a request value
The expiry ceiling; a shorter expiry is the approver's choice
The recorded revocation of the release's standing approval, beside the diff. Present instead of standingApproval when the release's recorded grant has been revoked, so a client that does not know this field shows no approval rather than one that still looks live.
The human whose act ended the grant
The recorded revocation: one entry per grant the release's current diff hash bounds, each naming the human whose act it was. The grants are not edited and not deleted: these records stand beside them, and the next mint is refused from revokedAt on.
retrieval_scope, denied_access, expired_credential, blocked_egress, output_formatThe stored output. A pass without an artifact is a claim.
Always true on failure
approve, rejectcustomer, nectoThe side of the two-approver rule the resolved authority record named when the decision was made. A widening release needs one customer and one necto approval by two distinct subjects; a non-widening release is approvable by the customer's own security owner alone.
Whether the release widened egress or tool authority against the release the platform ran when the decision was made. true marks a decision that needs one customer and one necto approval by two distinct subjects; an absent field is a one-party decision. Forward-only: a previous build refuses a record that states true rather than reading one party as two.
Whether the decision is the Necto-operator fallback: the operator approves only while no customer security owner resolves for the deployment. The record names the fallback explicitly and never claims the customer's approval; its party stays necto. Forward-only: a previous build refuses a record that states true rather than reading the operator as the customer's approval.
not-needed, wrong-scope, test-failure, citation-gone, unsafe-change, otherA rejection's structured reason code, from the closed set. An approval carries neither a code nor a reason text. The text beside it is stored and never parsed for ids or commands. Forward-only: a previous build refuses a record that states one rather than reading a rejection as an approval.
The rejection's free text, stored verbatim beside its code and escaped on render. It is never parsed for ids or commands. Forward-only: a previous build refuses a record that states one rather than reading an unreviewed word as the reason code.
One recorded collaboration grant: the application, the granted subject's issuer and subject, the role, the grant's content-hash version, the actor whose act it was and the time. Every field is an identifier — never a display name, an address or a claim. customerId is the deployment's own tenant key, recorded with the grant and never selected by the request.
viewing, editing, deploying, approving, stoppingThe content-hash identity of the grant state the act produced or removed
One clone act's result, as identifiers only: the clone's new stable application id, its new plan version hash and the source version hash the clone is parented to. Never a plan, a secret or any copied content.
sha256:<hex> of the clone's new plan version
The source version hash the clone is parented to
What a comment is attached to, by identifier: the anchor kind and the anchor's own id. kind is a closed set — an unknown kind is refused rather than filed. The id is an identifier, never the object it names.
plan_version, deployment, run, policy_versionOne comment's newest row, as identifiers and a count. redacted is true when the newest row is a redaction; revisionCount is how many rows the store holds — a count, never a prior body. body is the newest revision's content and is present only for a live comment: a redacted comment carries no body. redacts is the version of the revision a redaction hides and is present only when redacted is true; the hidden revision stays in the store, never mutated or deleted. The actor is a subject identifier, never a display name, and at is when the newest row was recorded.
The newest revision's content; absent for a redacted comment, because a redacted body is never returned.
The version of the revision this row hides; absent unless redacted is true, because a live comment redacts nothing.
The cap on runs admitted in one window.
The fixed window's length; a window is aligned to the Unix epoch, so the same instant maps to the same window everywhere.
The runs claimed in this window.
The runs this window still admits, derived from the cap and the window's own count rather than carried beside them.
One promotion hop, as metadata only. There is no field for a credential or persistent data, structurally: promotion carries an image digest and an accepted plan version, never either.
The requester's assertion-resolved subject
dev, stage, proddev, stage, prodThe platform-derived content hash of the application's registered governed profile, or the explicit value none when none governs it. Never a request field.
server-derived from the application's governed profile, never a request field; the private-e2b placement gate reads it
The platform's own compiled-artifact hashes for the registered governed profile in the target environment, or empty when none governs it. Never a request field.
standard, governedstandard, governedThe platform-computed placement
The platform-derived namespace for the target environment; supplied by the derivation seam, never by a caller.
What the two approvers bind: the combined deployment hash over the application, target environment, image digest, accepted plan and governed hashes.
approve, rejectThe target environment's head deployment record id at decision time — what the approval vouched for alongside the hash.
Where the application's code comes from — one of the four accepted sources. An identifier, never content: a repository location, a template identity, a Dockerfile location or a prebuilt OCI image addressed by immutable digest. No field here carries code, a credential or persistent data.
repository, template, dockerfile, oci_digestThe source's identifier: a repository location, a template id, a Dockerfile location, or a sha256:<hex> image digest — the immutable identity a prebuilt image is addressed by, never a tag.
One application: the primary customer object, one stable identity across dev, stage and prod. Metadata only — the record names its source, never code or a credential. Every record carries the deployment's tenant key; no request field selects it.
The assertion-resolved subject that registered it
One managed service's own state in one application environment: the logical resource name and the credential reference the platform derived. Identifiers only — no field carries a credential value and no field carries payload content. state is the platform's own word: provisioned, or failed when the provider did not answer — never reported as success.
dev, stage, prodpostgresql, object_storage, durable_queueprovisioned, failedThe managed state one application environment owns, verbatim from the platform: the three services' logical resource names and credential references, each with its recorded state. The environment is the deployment's own selection; another customer's application is not one this deployment holds.
dev, stage, prodThe one field setup names: the environment to set up. The names and references are the platform's derivation, never body fields.
dev, stage, prodThe secret's name and value, carried once. The value is held by the secret manager and never recorded, returned or echoed; the answer carries the reference instead.
dev, stage, prodThe value the secret manager holds, never recorded or echoed
What recording a secret answered: the secret's name and the reference the platform derived and the secret manager holds. The value is never in the answer and never in any record.
The visible, editable plan: topology only, never authority. No field grants a tool, source, destination or credential — necto.yaml remains the only authority source for governed components. The plan carries the deployment's tenant key; no request field selects it.
The deployment's tenant key
standard, governedThe requested runtime tier; placement is computed later
The source revision and build context, or the digest that is the source for a prebuilt image — topology, never authority.
repository, dockerfile, oci_digestsha256:<hex>; the immutable identity a prebuilt image is addressed by
A component's name and kind, and the one profile identity a governed component carries — a name, never the compiled profile.
web, api, worker, staticThe detector, builder and base-image identifiers. Identifiers, never measurements: synthetic adapter input records synthetic-adapter-input and claims no version.
A command with no schedule is a worker; one with a cron expression is a schedule
Resources and scaling bounds
A declared external destination for a standard component — a bare host name, topology only. It grants no reachability and routes no traffic.
One immutable plan version: the plan, its content hash and parent, and the winning source of every field. The hash is minted from the tenant key, the plan and the parent, so a version is never re-labelled and a correction is a new version, not a change to this one.
sha256:<hex> over the tenant key, the plan and the parent
The version this one corrects; absent for the first version
Every plan field's winning source, keyed by the plan's field name — the plan's own precedence display.
Where a field's winning value came from, verbatim from the plan's own precedence: the explicit repository file, the saved project setting, or the detector default. Every displayed field carries one.
The detection input. The application's registered source selects the construction path: adapterInput is the detector's output for a repository or template source, dockerfile is the file's text for a Dockerfile source, and a prebuilt image has neither — its digest is the source and the build is skipped. The detector output is adapter input, never the public schema: it is read against a closed contract, and a key outside it is refused rather than ignored. saved is the saved project setting level, read against the closed necto.app.yaml schema.
The detector's output for a repository or template source, read against the adapter's closed contract — never the public schema and never persisted as such.
The Dockerfile's text for a Dockerfile source; at most the minimal read (FROM, EXPOSE, CMD) resolves a value, and anything ambiguous is refused.
The saved project setting level, read against the closed necto.app.yaml schema; an unknown key is refused.
A customer correction of the current plan version. values is the closed necto.app.yaml schema — the correctable plan fields. A key outside it is refused as unknown; toolchain and source are deliberately not correctable, and customer_id is refused like any other unknown key because the deployment supplies the tenant key.
The closed necto.app.yaml value set
The read-only graph and the default guardrails for one application's accepted plan version. Every field states the source it came from and the accepted plan version it was read at: a graph field's source is the plan's own per-field provenance, a guardrail field's source is the governed profile (or default when none governs), and the placement's source is computed. The plan is topology only and grants no authority — no graph field is compiled into a guardrail.
The accepted plan version hash every field was read at
The governed profile's content hash when one governs the application; absent when none does
One graph or guardrail field, its value, the source the value came from and the accepted plan version it was read at.
The plan field's camelCase name for a graph field, or the guardrail's own dotted name (guardrails.actionAllowlist, guardrails.egressAllowlist, guardrails.credentialScopes, placement)
The value's winning source: the plan's per-field provenance level for a graph field, governed profile for a compiled guardrail, default when no profile governs, or computed for the placement. Never a plan field standing in for authority.
The accepted plan version hash this field was read at
A refusal the read made visible: the field it concerns, the policy decision's own reason, its safe metadata and the accepted plan version it was read at — never a generic application error.
The policy decision's own named reason
The profile field a compile refusal names
One deployment of the application's accepted plan. The plan version is named by its content hash and the image by immutable digest; the environment is the deployment's dev environment, and the governed profile hash and compiled-artifact hashes on the record are the platform's own derived values, never body fields.
The accepted plan version's content hash
sha256:<hex>; the built image this deployment runs
Test-name and artifact-URL identifier pairs
Every deployment's automatic record: metadata only — never source, prompt, response, secret or payload content.
dev, stage, prodTest-name and artifact-URL identifier pairs
The agent card's field set is fixed
Returned alongside runs, never behind a filter
drafted, approved, sent, refused, failedAn uncited draft cannot be promoted to a send
The schedule tick a governed run was started from. Absent on a run that was started without a trigger, which reads as absent rather than as a fabricated tick.
scheduleThe verified client principal that started the run, never a value the run's own request supplied.
A schedule tick a registered machine client starts a run from.
scheduleThe tick's instant, timezone-aware
The tick's canonical key, recomputed from the task id and the scheduled instant; a value that does not match is refused.
A tick a scheduler observed but did not fire, reported so it is recorded rather than silent.
skippedmissedThe tick's instant, timezone-aware
The tick's canonical key, recomputed from the task id and the scheduled instant; a value that does not match is refused.
retrieval_denied, egress_blocked, credential_expired, scope_refusedOne period of one agent's evidence, as an auditor reads it. Built only from TimelineEntry and release identifiers: there is no field here a document body or a raw prompt could occupy. Plain JSON, readable without Necto tooling.
The bundle format, so a reader can tell versions apart
The verified subject that asked for the export
Nothing before this is guaranteed to still exist
False when the period begins before the retention cutoff
Immutable. Identifiers and decisions only: no document text, no raw prompts, no credential values.
sandbox_created, sandbox_resumed, sandbox_timeout, sandbox_killed, sandbox_start_denied, sandbox_start_failed, sandbox_exec_failed, run_finished, sandbox_stop_failed, scope_checked, retrieval_decided, candidate_dropped, credential_injected, draft_produced, approval_decided, send_attempted, action_replay_refused, export_created, containment_triggered, workload_stopped, authority_revoked, service_restored, identity_asserted, authority_resolved, authority_granted, authority_withdrawn, release_decided, standing_approval_revoked, ttl_exception_granted, credential_withdrawn, api_request, caller_first_seen, promotion_requested, promotion_decided, promotion_executed, schedule_fired, schedule_skipped, schedule_refusedsandbox_resumed is reserved and never emitted: the runtime interface has no resume operation. Do not branch on it.
semantic, infrastructureThe entry's own class: semantic for a kind the four behavioural dimensions read, infrastructure for every other kind the record names. A word, never a number or a score. An entry presented as semantic agent evidence is checked before it is presented, and a refusal names the kind.
Which identity provider vouched for caller. A sub is unique only within its issuer, and a later dispute needs to know which provider asserted it. Under federation this is the origin, not the console, so a federated login's entry names the provider that actually asserted it.
The third part of the authority key. The same human may exist under more than one customer.
The agent an entry is about, where that is not implied by the run it belongs to. A containment event is the case that needs it: stopping an agent is not part of a run, and "which agent was contained" is the first question an incident review asks.
The workload a lifecycle entry is about. An identifier the substrate issued, never a substrate handle.
The standing approval a revocation entry ends. An identifier of the grant, never its channel, message or approver.
The prebuilt artifact the workload started from: an E2B template build id (a UUID), built outside the platform process, never a template name, alias or tag. The released identifier the sandbox was actually created from, so "what ran" is answerable from the record rather than from the release store's current state.
Who owns the agent, carried from the compiled metadata. Distinct from caller, which is whoever made the request an entry records.
The scheduled task a schedule_fired, schedule_skipped or schedule_refused entry is about. It names the task whose tick did or did not run.
The tick's scheduled instant, timezone-aware. A tick is an instant, not a period.
The tick's canonical key, recomputed from the task id and the scheduled instant. A repeated key never starts a second run. Absent on a tick refused before it could have run: no key names a tick that has not happened.
The declared purpose the query ran under. A decision is not reconstructable without it.
The environment the query ran in. Staging never reaches a production source; the record proves which one asked.
The application the record is about, from the deployment record's (customer, application, environment) triple. An identifier, never the application's source, configuration or content.
The plan component within the application the record is about. A component name, never the component's command, image or environment.
The deployment record the entry is about: the id a promotion approval records as its target head and the deployment store compares. An identifier, never the deployment's actor, plan or artifact.
Which connector made the source decision. An identifier, never a credential or an endpoint.
allow, denyRequired on a deny. A silent drop is indistinguishable from a retrieval miss and destroys the audit trail. On a tick refused before it could have run it is future.
Identifier only, never the token value
The action envelope an entry is about. An approval binds the envelope's hash, so a reviewer asking whether an action was executed, and executed once, has it in hand. The id is what joins send_attempted to action_replay_refused.
Salted
The citation set, as document@version#location identifier triples: the passages a step relied on, never the passages themselves.
An identity_asserted or authority_resolved entry's whole upstream identity path, as issuer#subject#verification_basis identifier triples, at most two, origin first. verification_basis is one of id_token_verified, broker_verified_origin or broker_authenticated. issuer and caller above already name the first entry; this carries the rest of the chain. Absent for a non-federated login.
HTTP method of the request an api_request entry records. An identifier of the call, never the call itself.
The matched route's path template, such as /agents/{agentId}/card, never the concrete path. Absent on an unmatched route, where only the concrete path exists.
The matched route's name. unmatched when no route matched: an unmatched route is a refusal, and its record has to say which path could not.
The response status the request produced.
served, refused, failedThe refusal/fault taxonomy, on api_request entries: served is a 2xx; refused is Necto considering a request and saying no (the 4xxs); failed is Necto unable to answer (the 5xxs). Not decision's retrieval enum.
under_100ms, 100ms_1s, 1s_10s, over_10sCoarse response latency, an ordered bucket, not the raw duration.
The artifact entrypoint's own exit result, recorded unchanged. Present only on a run that ended: 0 ran, and the entrypoint's own non-zero code is the entrypoint's own result, not a platform refusal.
The number of UTF-8 bytes the entrypoint wrote to stdout, never the output itself. Present only on a run that ended, so a silent refusal is visible without any output reaching the record.
The number of UTF-8 bytes the entrypoint wrote to stderr, never the output itself. Present only on a run that ended, so a silent refusal is visible without any output reaching the record.
The release regime a baseline and an observation are keyed to: one agent, one compiled profile and one release. A release opens a new regime; a prior regime's baseline is kept for comparison and never scores the next one.
A closed range. Both ends are included.
One refused attempt, carrying the rule that refused it. A refusal is an observation, never an absence: an attempt the platform refused is returned here rather than dropped from the comparison. Identifiers only, never content, passages, prompts or credentials.
The document, destination or tool identifier the attempt named, when the record names one.
The rule that refused the attempt, as the record states it.
One document a run read, or was refused.
read, refusedThe rule that refused the read; absent when it was read.
One destination a run contacted, or was refused.
sent, refusedThe number of attempts the run's own record carries with this name and result: the send attempts the platform allowed for a sent destination, and the attempts the named rule refused for a refused one.
The rule that refused the send; absent when it was sent.
One dimension's comparison verdict. neutral when the run's values are inside the regime's baseline; review when they are outside it or an attempt on the dimension was refused. Never a number.
neutral, reviewPlain words for a review verdict; absent when neutral.
The comparison verdict for each of the four dimensions.
Where one regime's baseline ended and the next began, derived from the agent's own run records.
The documents a usual run reads, and how many it opens.
Absent while the regime has no observed run.
The destinations a usual run contacts.
The tool order a usual run follows.
The records-per-run and run-seconds a usual run shows.
Absent while the regime has no observed run.
Absent while the regime has no observed run.
What is usual for one agent in one release regime. state is ready when the regime has enough observed history to compare against, and insufficient_volume when it does not: the state scores nothing, and no verdict is returned rather than a low one.
Absent when no run names a regime yet.
ready, insufficient_volumeWhy the state is insufficient_volume; absent when ready.
The number of the regime's finished runs this baseline summarises. The platform holds no separate review record, so this is an observation count and never a claim that a person reviewed each run.
The configured minimum the state is read against; absent when the deployment configures none, and then the state is insufficient_volume rather than ready.
The documents one run read, or was refused.
How many records the run opened.
The destinations one run contacted, or was refused.
The tools one run used, in order, and those refused.
The records one run opened and the seconds it ran.
Absent when the run has no recorded end.
One run's observed behaviour on the four dimensions, compared with the baseline of the regime that run belongs to, as it stood before the run started. Only that regime's runs that finished before this run started count. Refused attempts are observations carrying the rule that refused them, never dropped. state is insufficient_volume when the regime has too little observed history to compare against, and then comparison is absent: the run scores nothing rather than badly.
The regime this run was compared against.
ready, insufficient_volumeWhy the state is insufficient_volume; absent when ready.
The number of the regime's runs that finished before this run started, which the comparison baseline summarises. The platform holds no separate review record, so this is an observation count and never a claim that a person reviewed each run.
The configured minimum the state is read against; absent when the deployment configures none.
Absent while the state is insufficient_volume.
read_only, draft_only, write_reversible, write_externalPresent when allowed is true
Required when allowed is false
Always true: a 200 is returned only once every axis is confirmed a subset of the target agent's compiled artifacts. A response that would carry false is a 422 Problem instead.
The agent's last approved release version, when one exists
The task's own narrow.data, already confirmed a subset
The task's own narrow.tools, already confirmed a subset
read_only, draft_only, write_reversible, write_externalThe task's own requested authority
The profile's data minus the task's narrow.data
The profile's tools minus the task's narrow.tools
read_only, draft_only, write_reversible, write_externalThe profile's own authority ceiling
Present only when the applied task carried a schedule. The recorded schedule carries its invocation beside the narrowing, so a scheduler can reconstruct the run without the task file.
One recorded kind: Task apply that carried a schedule. The record carries the compiled invocation (input, deliver, authority, narrowed data/tools) and the tenant key, so a scheduler can reconstruct the run.
The deployment's own tenant key, recorded with the task. Deployment-supplied, never taken from a request, claim or header.
Five-field cron in UTC, validated by the standard-tier trigger validator at compile. A malformed schedule is refused by name, never recorded.
The task's own input, kept for the scheduler
The task's own deliver, kept for the scheduler
read_only, draft_only, write_reversible, write_externalThe task's own requested authority
The task's narrowed narrow.data
The task's narrowed narrow.tools
One collaboration mutation, as the append-only journal recorded it: the actor, the organisation, the application, the object kind and id, the action and the resulting version. Every field is an identifier — never a comment body, prompt or payload. A clone lands as a plan_version, never as a kind of its own.
The deployment's own tenant key, recorded with the mutation. Deployment-supplied, never taken from a request, claim or header. The read is already tenant-keyed; the key is carried so the record says what it is keyed by.
The first-class organisation the mutation belongs to, recorded beside the tenant key. Strictly inside the customer, never a second tenant axis; the record carries it so a caller can tell which organisation a mutation belongs to.
The mutation's assertion-resolved subject
comment, plan_version, role_grant, membership, componentcreated, cloned, granted, revokedOne derived version-history entry: the anchor kind, the identifier its store knows it by, the time the store recorded and the actor where the store has one. at and actor are absent where the owning store has neither — a plan version carries no clock and no actor, a run record carries no actor. parent is a plan version's lineage by hash. Nothing here is invented to fill a gap.
plan_version, deployment, release, runThe stable subject identifier this caller resolved to. Under federation this is the origin entry's sub on the assertion, not the console's.
Whether this caller may approve a release hash
One record of the committed federation registry, rendered for the console server that reconciles its provider list against it. Identifiers and display fields only, never a secret: the upstream issuer, the record's Necto-side customer id, the named endpoint hosts and the allowed verification bases. No client credential, domain or endpoint path is in the record or the answer.
The upstream identity provider's issuer identifier, as the record holds it.
The Necto-side customer the record is for; every record this deployment answers carries the deployment's own.
The named host serving the issuer's JWKS
The named discovery endpoint host, when configured
The named token endpoint host, when configured
The named user-info endpoint host, when configured
The verification bases the record allows, e.g. id_token_verified; the console's own basis must be among them.
One refused destination, identifier-only. The destination's own identifiers and the platform's own instants, plus the bounded attempt counter; no payload, content or capture field exists. The tenant key, the run's governed-component identity and the run come from the deployment and the verified workload claims, never from a request body. The attempt counter is a bounded count, never a score. A repeat observation advances lastSeenAt and never moves observedAt; capReached marks the run's single cap-reached record, written once when a new destination arrives past the per-run cap.
The stable identifier for this observed destination; the decision record and the customer-plane routes name it.
The deployment's own customer id
The run's governed-component identity -- the profile name the run route resolved -- never a body field
The run whose egress refused the destination
The named host the run tried, normalized; never a wildcard, a CIDR, a scheme, a port or a path
tcp, tlsThe platform's own instant at the first observation; a repeat counts and never moves it
The platform's own instant at the most recent observation; a repeat advances it
The bounded attempt count; a repeat observation coalesces into the row and increments it up to the platform's cap
The run's one cap-reached record: true when a new destination arrived past the per-run cap, so the refusal list is bounded and the cap is recorded rather than sampled