Agent Lease Manifest
What an agent DECLARES it needs before the kernel lets it run. The manifest is the declared half of a two-part record: the agent (or the bridge or harness presenting on its behalf) declares tools, paths, hosts, commands and budget, and the kernel answers with an Agent Lease whose manifest is the GRANTED half, narrowed to what the parent lease and the workspace policy allow. Declared and granted have the same shape on purpose, so the narrowing diff is a plain comparison. Rule 7 of the harness: no manifest, no lease; no lease, no connectivity. A manifest the kernel cannot narrow to a non-empty grant is refused, and the refusal is recorded. Paths are workspace-relative POSIX globs; hosts are lower-case DNS names (IPv4 literals and localhost included) with an optional single leading wildcard label; commands are executable basenames; tools are identifiers of at most 128 characters and never *.
- Schema
agent-lease-manifest.schema.json, served as JSON at its$id: https://cognitive-delivery.github.io/contract/1.x/agent-lease-manifest.schema.json- Dialect
http://json-schema.org/draft-07/schema#- Root
- object, open (additional properties are carried)
required:schema_version,agent,intent,allow,deny,budget,approvals - Stability
- none stated at the root; every declared property carries its own
- Properties
- 39 declared: 9 under the root, 30 in definitions
Properties
Every declared property under the root, in the schema's own order. [] is an array's items, .* the shape of every unnamed member, #name a definition. Objects carry additional properties unless a row says otherwise.
| Path | Type | Required | Description | Constraints | Stability |
|---|---|---|---|---|---|
schema_version | string | yes | Contract version this record was written against, as major.minor. One rule across every schema (schema set 1.2): a reader compares the major only, and a minor it has not met is additive by the contract's own rule. | pattern ^\d+\.\d+$ | stable |
agent | object #agent | yes | Who is asking. name is a label for the cockpit; kind says how the harness runs it; runtime_agent is the existing closed agent vocabulary so a lease keys to the same identity every audit event already carries. | stable | |
parent_lease_id | string | The lease this manifest is presented under, when it is a child. A root manifest has none and is narrowed against the workspace policy instead. | pattern ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ | stable | |
intent | object #intent | yes | Why the agent is running. purpose is required and non-empty: a manifest with no stated purpose is refused. | stable | |
allow | object #allow | yes | What the agent asks to be allowed. Every list is required and may be empty; an empty tools list in a GRANTED manifest is a lease that opens nothing, which is why the kernel refuses rather than grants it. Granted lists are always a subset of the parent's. | stable | |
deny | object #deny | yes | What the agent must not do even if allow would otherwise permit it. Granted deny lists are the UNION of the declared and the parent's, so a child can never shed a parent's refusal. | stable | |
budget | object #budget | yes | Ceilings. Every field is optional and non-negative; an absent field means the parent's remaining budget applies. A child's granted budget is clamped to its parent's remaining, depth is decremented per generation and fan_out is decremented on the parent as each child is issued. | stable | |
approvals | array | yes | Action names that need a human before the agent may perform them, under the same identifier rule as tool names. Required and may be empty. | stable | |
approvals[] | string | An action that needs a human first; the same identifier rule as a tool name. At most 128 characters of letters, digits, _, ., :, / and -, so a governed cdf_ name and a <server>/<tool> pair both fit. * is refused: a lease that names every tool has not named one, and SPEC §5.2 R2 exists because a grant must say what it opens. Whitespace is refused. | pattern ^[A-Za-z0-9_][A-Za-z0-9_.:/-]{0,127}$min length 1 | ||
attestation | object #attestation | Who vouches for the declaration. Optional: an agent presenting its own manifest has nothing to sign with, and the bridge or harness signs on its behalf. | stable |
Definitions
The named shapes this schema refers to as #name. A row above that links here is not expanded in place; its constraints are the definition's.
#model
| Path | Type | Required | Description | Constraints | Stability |
|---|---|---|---|---|---|
#model | object | The model behind the agent, for attribution. Never a prompt, never a key. | |||
#model.vendor | string | yes | min length 1 | stable | |
#model.family | string | yes | min length 1 | stable | |
#model.version | string | stable |
#agent
| Path | Type | Required | Description | Constraints | Stability |
|---|---|---|---|---|---|
#agent | object | Who is asking. name is a label for the cockpit; kind says how the harness runs it; runtime_agent is the existing closed agent vocabulary so a lease keys to the same identity every audit event already carries. | |||
#agent.name | string | yes | Display label for the cockpit, at most 120 characters: the worker role, the session, the plugin. Not an identity, and never a person's name, email address or git identity; this value is recorded in an audit journal that is retained indefinitely. | min length 1 max length 120 | stable |
#agent.kind | string | yes | How the harness runs this agent. native: inside the kernel's own process. delegated-cli: a spawned coding CLI. plugin: loaded into the host. external: presented over a bridge from outside the harness. | one of "native", "delegated-cli", "plugin", "external" | stable |
#agent.runtime_agent | string | yes | The closed agent identity vocabulary. unknown is a first-class value, never an error: the kernel looked and could not tell. cdf-native (schema set 1.0, additive) is the harness's own native turn loop: the kernel drives a model provider directly, with no vendor CLI or SDK. | one of "codex-cli", "claude-code", "gemini-code-assist", "github-copilot", "cdf-native", "unknown" | stable |
#agent.provider | string | The organisation or product providing the agent, when it differs from the runtime. | stable | ||
#agent.model | object #model | The model behind the agent, for attribution. Never a prompt, never a key. | stable | ||
#agent.harness_version | string | Version of the harness that generated or presented this manifest. | stable |
#intent
| Path | Type | Required | Description | Constraints | Stability |
|---|---|---|---|---|---|
#intent | object | Why the agent is running. purpose is required and non-empty: a manifest with no stated purpose is refused. | |||
#intent.spec_slug | string | The governed spec this run serves, when there is one. | stable | ||
#intent.task_id | string | The task within that spec, when there is one. | stable | ||
#intent.purpose | string | yes | One sentence of intent, at most 500 characters. Recorded in the audit journal, so no prompt and no content. | min length 1 max length 500 | stable |
#allow
| Path | Type | Required | Description | Constraints | Stability |
|---|---|---|---|---|---|
#allow | object | What the agent asks to be allowed. Every list is required and may be empty; an empty tools list in a GRANTED manifest is a lease that opens nothing, which is why the kernel refuses rather than grants it. Granted lists are always a subset of the parent's. | |||
#allow.tools | array | yes | Governed tool names the agent may call. An empty list in a GRANTED manifest is refused (SPEC §5.2 R2). | stable | |
#allow.tools[] | string | A governed tool name the agent may call. At most 128 characters of letters, digits, _, ., :, / and -, so a governed cdf_ name and a <server>/<tool> pair both fit. * is refused: a lease that names every tool has not named one, and SPEC §5.2 R2 exists because a grant must say what it opens. Whitespace is refused. | pattern ^[A-Za-z0-9_][A-Za-z0-9_.:/-]{0,127}$min length 1 | ||
#allow.read_paths | array | yes | Workspace-relative POSIX globs the agent may read. Refused here, without lookahead so any RE2-based validator can load the rule: a leading /, any .. segment, a leading ~, a drive-letter prefix and any backslash. The registry re-checks the resolved path against the workspace root. | stable | |
#allow.read_paths[] | string | min length 1 must not match ^/, (^|/)\.\.(/|$), ^~, ^[A-Za-z]:, \\ | |||
#allow.write_paths | array | yes | Workspace-relative POSIX globs the agent may change. Refused here, without lookahead: a leading /, any .. segment, a leading ~, a drive-letter prefix and any backslash. The registry re-checks the resolved path against the workspace root, and a change outside the granted set is a scope violation that revokes the lease. | stable | |
#allow.write_paths[] | string | min length 1 must not match ^/, (^|/)\.\.(/|$), ^~, ^[A-Za-z]:, \\ | |||
#allow.hosts | array | yes | Hosts the agent may reach. *.example.com matches api.example.com and not example.com; matching a bare parent domain requires listing it. Enforced by the egress proxy once it keys off the lease. | stable | |
#allow.hosts[] | string | A host the agent may reach. A lower-case DNS name of one or more labels, an IPv4 literal or localhost (both are DNS-name shaped), with an optional single leading *. label, or the bare *. Refused by the pattern, which uses no lookahead or backreference and compiles under RE2: a scheme, a port, a path, whitespace, upper case, a trailing dot and a wildcard anywhere but the first label. A port is not a host: the egress proxy matches hostnames, and a rule nothing enforces is a claim. IP literals are admitted because a local model provider (Ollama, LM Studio) lives at 127.0.0.1 and the root policy derives its hosts from provider URLs. | pattern ^(\*|(\*\.)?[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*)$min length 1 max length 253 | ||
#allow.commands | array | yes | Executables the agent may run, by basename. | stable | |
#allow.commands[] | string | An executable the agent may run. The basename of the executable, at most 128 characters: letters, digits, ., _, +, -. No path separator (identity is the resolved executable's basename, not where it was found), no whitespace (an argument is not part of the name) and no shell operator. | pattern ^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$min length 1 | ||
#allow.tool_args | object | DECLARED argument constraints per tool (schema set 1.2, development stability): tool name to a JSON Schema the agent proposes for its own calls to that tool, after Progent's argument-schema policies. A declaration, not a grant: in 1.x an issuer MAY omit it from the granted manifest and MUST NOT treat it as granted authority, because the reference gate does not yet evaluate argument schemas and a narrowing rule without an enforcing gate is a claim the corpus cannot test. The vector tool-args-dropped shows the reference dropping it. It becomes a rule when a gate enforces it. | keys must match ^[A-Za-z0-9_][A-Za-z0-9_.:/-]{0,127}$additional properties: see #allow.tool_args.* | development a declaration an issuer MAY omit from the grant (SPEC 4.4); becomes a rule when a gate enforces it | |
#allow.tool_args.* | object | A JSON Schema. Not validated as one here: draft-07 cannot validate a schema as data without a meta-schema $ref, which the self-contained rule forbids. |
#deny
| Path | Type | Required | Description | Constraints | Stability |
|---|---|---|---|---|---|
#deny | object | What the agent must not do even if allow would otherwise permit it. Granted deny lists are the UNION of the declared and the parent's, so a child can never shed a parent's refusal. | |||
#deny.commands | array | yes | Executable names refused outright, for example sudo, su, docker, ssh, curl, wget, launchctl, systemctl. | stable | |
#deny.commands[] | string | An executable refused outright. The basename of the executable, at most 128 characters: letters, digits, ., _, +, -. No path separator (identity is the resolved executable's basename, not where it was found), no whitespace (an argument is not part of the name) and no shell operator. | pattern ^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$min length 1 | ||
#deny.paths | array | yes | POSIX globs the agent must not touch: credentials, governance evidence, the lease journal itself. A denial MAY be home-relative (~/.ssh/**), because refusing a path outside the workspace is meaningful even though no allow could grant it; a leading /, any .. segment and any backslash are refused. | stable | |
#deny.paths[] | string | min length 1 must not match ^/, (^|/)\.\.(/|$), \\ | |||
#deny.hosts | array | yes | Hostnames refused outright. | stable | |
#deny.hosts[] | string | A host refused outright. A lower-case DNS name of one or more labels, an IPv4 literal or localhost (both are DNS-name shaped), with an optional single leading *. label, or the bare *. Refused by the pattern, which uses no lookahead or backreference and compiles under RE2: a scheme, a port, a path, whitespace, upper case, a trailing dot and a wildcard anywhere but the first label. A port is not a host: the egress proxy matches hostnames, and a rule nothing enforces is a claim. IP literals are admitted because a local model provider (Ollama, LM Studio) lives at 127.0.0.1 and the root policy derives its hosts from provider URLs. | pattern ^(\*|(\*\.)?[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*)$min length 1 max length 253 |
#budget
| Path | Type | Required | Description | Constraints | Stability |
|---|---|---|---|---|---|
#budget | object | Ceilings. Every field is optional and non-negative; an absent field means the parent's remaining budget applies. A child's granted budget is clamped to its parent's remaining, depth is decremented per generation and fan_out is decremented on the parent as each child is issued. | |||
#budget.steps | integer | Maximum tool calls or turns. | min 0 max 9007199254740991 | stable | |
#budget.wall_clock_ms | integer | Maximum lifetime in milliseconds; the lease expires when it is spent. | min 0 max 9007199254740991 | stable | |
#budget.tokens | integer | Maximum model tokens, input plus output. | min 0 max 9007199254740991 | stable | |
#budget.cost_usd | number | Maximum spend in US dollars. | min 0 | stable | |
#budget.depth | integer | How many generations of child lease may be issued beneath this one. Zero means no children. At most 16: the reference root policy allows 4, and a declaration above the ceiling is nonsense rather than something to clamp. | min 0 max 16 | stable | |
#budget.fan_out | integer | How many child leases may be live under this one at once. At most 256: the reference root policy allows 8. | min 0 max 256 | stable |
#attestation
| Path | Type | Required | Description | Constraints | Stability |
|---|---|---|---|---|---|
#attestation | object | Who vouches for the declaration. Optional: an agent presenting its own manifest has nothing to sign with, and the bridge or harness signs on its behalf. | |||
#attestation.issuer | string | yes | Which party produced the declaration. | one of "agent", "bridge", "harness", "plugin" | stable |
#attestation.signature | string | Lower-case hex signature over the canonical bytes of the manifest, when the issuer can sign: 64 hex characters, which is HMAC-SHA256 like the lease's own signature. An odd-length or longer value is not a signature this contract defines. | pattern ^[0-9a-f]{64}$ | stable | |
#attestation.key_fingerprint | string | Fingerprint of the key that produced signature. Never the key. | min length 1 | stable |