QuickAgents¶
This page covers the package format. The complete runtime side — every command, flag, policy key, MCP profiles, and enforcement mechanism — is the Runner reference.
A QuickAgent is a single-purpose AI agent packaged for Agentry: one manifest declaring what it is and what it may touch, the instruction itself, and any support files the manifest lists. QuickAgents carry no tool code. At run time the Agentry runner serves exactly the tools the manifest declares through an isolated MCP gateway, and when a QuickAgent is installed into an existing coding agent, the host's built-in tools stand in under the same declared contract.
Start from the QuickAgent template
(github.com/blaktron/agentry-template), a runnable package with every
option below shown in a commented agent.yaml. Or scaffold one with
agentry init, have the built-in Drafter write one with agentry draft
"<what it should do>" (the runner reference),
or use the Builder on the site or in the desktop app.
agentry init --from PATH imports an existing source instead of
scaffolding. From an Agent Skills SKILL.md it maps the frontmatter name,
description, license and metadata.version, copies the sibling files and
lists them in publish.include. From an MCP registry server.json it
writes a QuickAgent that declares the published package under
capabilities.mcpServers with its exact pin and the secret-flagged
environment variable names. The capability contract is never guessed from
the source: on a terminal the wizard asks for egress, secrets, exec and
filesystem with hints found in the source as defaults you confirm;
non-interactively pass --egress and --secrets (none allowed;
--exec, --fs-read, --fs-write optional) or the import refuses and
prints the hints.
agentry setup # one-time: LLM provider + the docker MCP gateway
agentry validate . # the manifest rules and the offline scanners (S1/S3/S4)
agentry run --local . --dry-run --allow-unsigned # the compiled enforcement plan
echo "task" | agentry run --local . --allow-unsigned --out ./agentry-out --file notes.pdf
agentry review . # the built-in Examiner, before you submit
agentry publish # scanned, signed, immutable
A first agentry publish needs the QuickAgent's record, which the CLI does
not create: make it at /new on the site first (Publishing).
Machine setup (agentry setup)¶
One idempotent command prepares a machine for QuickAgent runs before any
agent is involved. It writes your preferred LLM provider into the
operator policy (interactively on a terminal; --provider, --model,
--api-key-env, --base-url with --yes in scripts), then stands up the
docker-mcp-gateway plane right away — pulling the pinned gateway and
harness images and building the local runner image. The key itself is
never stored; only the environment variable name that will hold it.
| provider | default model | key env var | agent-container egress |
|---|---|---|---|
anthropic |
claude-sonnet-4-5 | ANTHROPIC_API_KEY |
api.anthropic.com:443 |
openai |
gpt-5.2 | OPENAI_API_KEY |
api.openai.com:443 |
google |
gemini-3-pro | GEMINI_API_KEY |
generativelanguage.googleapis.com:443 |
openrouter, poe, huggingface |
none: you type the model id | OPENROUTER_API_KEY, POE_API_KEY, HF_TOKEN |
openrouter.ai, api.poe.com, router.huggingface.co :443 |
copilot |
asked | none: your Copilot plan, signed in with GitHub | the Copilot API host of your plan |
custom |
asked | asked | derived from --base-url |
Instead of a key, a local run can be paid by your own ChatGPT, Claude or Copilot plan, or by an account you connect at OpenRouter, Poe or Hugging Face, signed in through your browser. These are model sign-ins; setup asks which way to pay where there is a choice.
For an OpenAI-compatible endpoint (a gateway, a hosted open-weight
model, a vendor's compatible mode) use --provider openai --base-url
https://…/v1 --model <id>: the provider name is what the harness sees, and
openai with a base URL is its OpenAI-compatible mode. The key reaches the
harness under the same variable name you give --api-key-env, and the
harness's providers read their conventional names — so name it
OPENAI_API_KEY for that path. The agent container's egress allowlist is
derived from the base URL's host.
agentry run still self-heals (it performs the same steps lazily on first
use), so setup is a convenience, not a gate. The desktop app drives the
same configuration from its Settings surface.
MCP profiles — keys and identities, once per release¶
The first run of a QuickAgent that declares secrets starts a guided
setup: the runner shows the declared contract, collects each key or
identity (input hidden, values stored only in the profile, 0600), and
asks whether to keep the result. The stored MCP profile is keyed by
signature — ns/name@version plus the archive digest — so:
- keep (the default): the next run of the same exact release uses the authenticated resources — no re-prompting;
- remove: the profile is deleted when the run completes.
The choice is configurable at install time: --keep-profile=true|false on
agentry run (including --prepare), the guided setup's final question,
or the policy default profiles.keepByDefault. A different digest is a
different trust decision and gets a fresh guided setup. Manage stored
profiles with agentry profiles list and agentry profiles remove
<ns/name[@version]> (secret values are never printed); re-run the guided
flow with --reconfigure-profile. A non-interactive run skips the guided
setup: secrets resolve from the environment or the policy's env file, and a
missing one refuses the run (exit 11) rather than degrading.
Package layout¶
my-quickagent/
├── agent.yaml # the manifest (required)
├── AGENT.md # the instruction file (entrypoints.instruction)
├── README.md # rendered on the registry page; required for public publication
├── LICENSE # required for public publication
├── output.schema.json # optional: structured-output contract
└── knowledge/<name>/ # optional: a knowledge pack the release ships
Support files (scripts, templates, examples) ship too when
publish.include lists them; the archive holds nothing else.
The manifest, option by option¶
agent.yaml uses manifest schema v2, a closed schema: unknown keys
are rejected, so a typo cannot silently disable a control. Releases
published under schema v1 (the kind-classified manifest) stay readable
forever — the registry maps skill and prompt-agent to
modes: [autonomous] — but a v2 manifest is what you write today, and
only a v2 manifest can declare interactive.
Identity and metadata¶
| Field | Required | Meaning and rules |
|---|---|---|
schemaVersion |
yes | the manifest schema major: "2" (v1 is read, not written) |
name |
yes | lowercase [a-z0-9-], ≤65 chars, starts/ends alphanumeric; unique within the namespace |
namespace |
yes | the publishing handle (a user or org), lowercase [a-z0-9-], ≤39 chars |
modes |
yes | the run modes the release supports: [autonomous], [interactive], or both (section below) |
version |
yes | strict semver without a leading v; equals the git tag when publishing from a tag; must exceed every existing version on its channel |
summary |
yes | one plain-text line, ≤140 characters |
license |
public publish | SPDX identifier or expression (MIT OR Apache-2.0); a LICENSE file must be present (its text is not compared) |
authors |
no | [{name, email?}, …] |
homepage |
no | informational URL |
tags |
no | lowercase [a-z0-9-] search facets |
Modes and the instruction file¶
A QuickAgent is an instruction package. What varies is how it runs, and a release declares the set of run modes it supports:
| Mode | What a run is | Status |
|---|---|---|
autonomous |
starts from its files — after the up-front questions, if any, and MCP profile authentication — does the job, writes its output and receipts, and ends | this is agentry run today |
interactive |
starts from the same plan and controls, but the run is a conversation: in the CLI a chat in the terminal (agentry run --mode interactive), in the desktop the run panel's chat. The agent can ask for information as the work reveals the need; you can answer and drop files into the read-only inputs folder (/add PATH), then /finish for the final result. You cannot give it more access than the manifest declares — the capability contract, the gateway and the egress allowlist are exactly those of an autonomous run of the same release, and a permission request from inside the sandbox is granted only for a tool the plan already serves, refused otherwise |
agentry run --mode interactive; a release with one mode runs in it, one with both is asked on a terminal and runs autonomous otherwise |
The instruction is one file:
| Field | Meaning |
|---|---|
entrypoints.instruction |
the repo-relative instruction file, AGENT.md when omitted. A leading YAML frontmatter block (the Agent Skills SKILL.md shape) is stripped; the body becomes the harness instruction, so a SKILL.md works unchanged as entrypoints.instruction: SKILL.md. The file must exist and be matched by publish.include |
There are no other package kinds. An MCP server is something a QuickAgent
uses — declared under capabilities.mcpServers and installed with the
release — not a package of its own.
intake: questions asked before a run¶
Both modes may ask a few things up front. The runner asks them through
the same prompter that collects MCP profile secrets (a terminal prompt in
the CLI, a form in the desktop) and stages the answers read-only for the
agent at /inputs/intake.json.
intake:
- id: dataset # [a-z0-9_]+, unique
prompt: "Path to the dataset to analyse"
type: file # text (default) | secret | choice | file | host | repository | url
required: true
- id: region
prompt: "Which region?"
type: choice
choices: [emea, apac, amer]
| Rule | |
|---|---|
id |
[a-z0-9_]{1,64}, unique within the manifest |
prompt |
required; one plain line, with no control character (an escape, a carriage return, a newline) and no format character (a bidi override, a zero-width space), since the CLI prints it on the terminal before the run and the desktop shows it as written (V10) |
type |
text, secret (injected as a run-scoped secret, never written as text or recorded; the upper-cased id must be listed under capabilities.secrets, or the run refuses it; and required must agree with that secret: true for a required secret, absent or false for one under capabilities.optionalSecrets, or publishing refuses it, V10), choice (needs choices), file (a host path the runner stages into the inputs folder), host (a target host, below), repository and url (an input source, below) |
choices |
only with type: choice; each one plain line, as prompt |
| at most 20 questions |
Non-secret answers are part of the run's receipts.
Input sources: repository and url¶
An input source is where a run's inputs come from. Every run has one,
the files a person uploads. Two more kinds are asked for by an intake
question whose type names the kind:
intake:
- id: repo
prompt: "The repository to audit"
type: repository # an https git URL and an optional ref
required: true
- id: advisory
prompt: "The advisory to check it against"
type: url # one https URL
| Type | The answer | What the agent is to get |
|---|---|---|
repository |
an https git URL and an optional ref (a branch, a tag or a commit) | the repository's files at that ref under /inputs/<id>/ |
url |
one https URL | the file at that address under /inputs/<id>/ |
Which runners fetch them. The CLI fetches a repository answer from
v0.6.97 and a url answer from v0.6.98;
the runner reference
describes the clone, the download, their caps and the token's path. A run
in Agentry fetches them too, on its sandbox host. A CLI or desktop older
than the core release that added the two types refuses a manifest
carrying either as an unknown intake type.
- The id names the folder. A runner that supports sources fetches the
answer before the run starts into
/inputs/<id>/, where the input tools read it as they read an upload. V10's id rules (the pattern, which allows no.or/, and uniqueness) therefore give each source a folder of its own. - The token belongs to the run. The run setup asks for an optional access token beside the answer, for a private repository or file. A release never declares it, and it is used by the fetch step alone.
- A source grants nothing. Giving the source is the grant, as
attaching a file is. A source question needs no
capabilitiesentry: no egress to the source's host and no secret for its token. Adding or removing one leaves the capability diff unchanged, so a new version that adds one asks for no new consent; the person running it chooses the source. The agent's ownfetchstays inside the contract as before. - A
repositoryquestion expects code. Wheninputs.kindsis declared and takes neithercodenorany, validation warns, without failing, that the release does not say it takes code (V13). choicesare refused on a source question, as on every type butchoice(V10).
Target hosts: host¶
A target host is a machine the person names when a run starts, such as
a Linux server for the QuickAgent to set up or manage. The release says
that it takes one, never which one, so one QuickAgent serves every server
its users have. It is declared in two places that name each other: an
intake question of type host, and its id under
capabilities.targetHosts.
intake:
- id: server
prompt: "The server to set up"
type: host # the person answers [user@]host[:port]
required: true
capabilities:
targetHosts: [server]
- The answer belongs to the run. The person writes
[user@]host[:port](an IPv6 address in brackets) when the run starts, and gives its credential then. The user defaults to theirs and the port to 22. The manifest never carries an address, a user, a port or a credential. - The runner makes the connection, not the agent. A runner that supports target hosts opens the SSH session from a container of its own, and the agent works the machine through the runner's host tools, a command at a time or in a shell. Target hosts are for local runs: a hosted run refuses a release that declares one.
- A target host is part of the contract. Unlike a source, it lets the
agent act on the machine: run any command the account can run there. So
the install consent shows it, a new version that adds one asks for
consent again, the registry's AI Review reads it as a declared
capability, and
GET /search?capability=targetHostsfinds such releases. It is not network egress. A support script the release ships that runssshitself is still the release reaching a host on its own, and the scan reports it as such (stage S6). - The rules (V14). Every
hostquestion is listed incapabilities.targetHosts, and every entry there names ahostquestion, once. A release declares at most four.choicesare refused on ahostquestion, as on every type butchoice(V10). Anexecdeclaration that namesssh,scp,sftporrsyncis warned, without failing, towardtargetHosts, and the scan reports it low (S1).
Which runners reach them. The CLI reaches a target host from v0.6.105:
the host link and its tools (host_run, host_read, host_write,
host_put, host_get) are in
the runner reference,
and from v0.6.106 so is an interactive shell on the host
(host_shell_open, host_shell_send, host_shell_read,
host_shell_close). A run in Agentry refuses a target host. A CLI or desktop older than the core release that
added the type refuses a manifest carrying it as an unknown intake type.
The browser's site: a url question named in capabilities.browser¶
A QuickAgent that tests websites declares a browser: a headless
Chromium the runner gives the run, driven through the runner's browser
tools. The site to test is named by the person when the run starts, as the
answer to a url question listed under
capabilities.browser.sites:
intake:
- id: site
prompt: "Which site should I test?"
type: url
required: true
capabilities:
browser:
sites: [site] # url questions: the person's answer is the site
hosts: [fonts.googleapis.com, "*.stripe.com"] # fixed hosts its pages need
scope: site # the default; or public
secrets: [SITE_USER, SITE_PASSWORD] # declared secrets it may type into the site's forms
secrets: [SITE_USER, SITE_PASSWORD]
- Where the browser may go. In
sitescope, the default, the site the person names, its subdomains, and thehoststhe release lists, each an exact name or a*.suffix. Inpublicscope, any public site. The runner holds the scope in a proxy of its own, the browser's only route out; private addresses are refused, except in a local run the site the person typed, which may be on a LAN or a Tailscale network. - Signing in.
secretsnames declared secrets the browser may type: the agent passes a secret's name and the runner's browser server types its value, which the agent never reads back. A browser that types secrets cannot havepublicscope, so the contract admits a secret only on the person's site, its subdomains and the declared hosts. The secrets are stored and asked for as any declared secret is. - The browser is part of the contract. The install consent shows the
line, a new version that adds a browser, a site, a host or a secret, or
moves from
sitetopublic, asks for consent again, the registry's AI Review sees it among the declared capabilities, andGET /search?capability=browserfinds such releases. It is not network egress: the tools container'sfetchstill followsnetwork.egress, and a support script that reaches a host itself is still the release's own network (stage S6).publicscope is held at publication as"*"egress is: under the platform's policy it goes to a reviewer. - The rules (V15).
schemaVersion: 2.sitesnamesurlquestions, at most four, each once.hostsare DNS names of two labels or more, or*.suffixes of one, at most 32: no"*", no address, no single-label name such aslocalhost, no port, and no suffix that is itself a public suffix (*.co.uk,*.github.io), which would admit every site registered under it.scopeissiteorpublic. The line names at least one site or host, orpublic. Each name insecretsis also incapabilities.secretsoroptionalSecrets, andsecretswithpublicis refused.
What a runner does with it. The core accepts the line and checks it as
above. From agentry-cli v0.6.117 a local run gives the release its browser: Microsoft's Playwright MCP server on the
pinned agentry-browser image, whose one route out is a browse proxy of the
run's own, the site answer opened and never downloaded, the browser secrets
typed by name, and the screenshots and PDFs in reports/browser/
(the runner reference).
From agentry-cli v0.6.118 and the enterprise build's v0.6.57 a run in
Agentry gets one too, its sites public addresses on ports 80 and 443 only
(hosted runs). A CLI or desktop
older than this core release
refuses a manifest carrying the line as an unknown field. public scope
runs only on a machine whose runner policy sets browser.allowPublicWeb.
interactive: session hints¶
interactive:
opening: "Which incident, and what do you need from it?" # ≤ 500 characters
idleTimeoutSeconds: 900 # policy caps it
opening is the assistant's first message when a session starts with no
task text. It may span lines and use tabs, and carries no other control
character and no format character, as the guide's overview does (V10).
Everything else about a session — budgets, idle timeout, wall
time — is the operator's runner policy, which a manifest can only
tighten. A release that does not list interactive may still declare
hints: validation warns, and they never apply.
guide: what a person reads before a run¶
A few lines in the author's own words for the run setup, the steps a
person goes through on the site and in the desktop before a run starts
(Before you start, Files, Connect tools, Details). It is shown to people
only: it never reaches the agent, the run plan, the capability diff or
the consent, so changing it asks no one to consent again. Without a
guide, the clients derive the same steps from the summary, the intake
questions and the declared capabilities.
guide:
overview: | # markdown, ≤ 4096 bytes
Reads your contract and your playbook and returns a clause-by-clause
review with the asks written out.
prepare: # ≤ 10 plain lines of ≤ 200 characters
- The contract, as text
- Your negotiation playbook, if you have one
files: "The contract, and optionally your playbook." # ≤ 500 characters
task: "e.g. 3-year SaaS subscription, 200 seats, EU data" # the task field's placeholder, ≤ 200 characters
connect: # help per declared secret, markdown, ≤ 2,000 characters each
GITHUB_TOKEN: |
A fine-grained token with **read** access to pull requests:
GitHub → Settings → Developer settings → Fine-grained tokens.
| Rule | |
|---|---|
overview, connect values |
markdown a person reads, never the agent; clients render it with no HTML and no images; a connect value is at most 2,000 characters (counted as characters, not bytes) and may not be empty or blank |
prepare, files, task |
one plain line each: no control characters and no format characters (bidi overrides, zero-width characters); a prepare item may not be empty or blank |
overview and connect characters |
newlines and tabs, and no other control character (no ESC, CR, BEL, NUL, DEL or C1 control); no format character |
connect keys |
each must be a secret the release declares, under capabilities.secrets or a declared MCP server's secrets |
| schema | v2 only; a v1 manifest carrying guide is refused (V1) |
The guide is validated under V10 with the intake and the interactive hints. A CLI or desktop older than the core release that added it refuses a manifest carrying one as an unknown field, in the release's archive and in the registry's answer about it, so use it once your tools are current.
knowledge: packs the release ships¶
A release may carry its own reference set: one or more
knowledge packs made with the Knowledge Creator
(agentry knowledge create), each in the archive under
knowledge/<name>/ exactly as the CLI keeps it: manifest.json, wiki/
with its index.md and log.md, and sources/. The runner mounts each
read-only at /knowledge/<name>, searched with the three knowledge tools
(CLI v0.6.11 or later). The block also says what a person
may attach of their own, which the run setup shows and the agent never
sees.
knowledge:
ships: # ≤ 5 packs, each in the archive under knowledge/<name>/
- name: aws-docs
accepts: # optional: what to attach, shown to the person only
prompt: "Your own runbooks, if you have them" # one plain line, ≤ 300 characters
required: false # true: the setup's Knowledge step cannot be skipped
publish:
include: [agent.yaml, AGENT.md, knowledge/**] # the packs must be in the archive
| Rule | |
|---|---|
ships[].name |
a pack name (1–64 lowercase letters, digits, ., - or _, starting with a letter or digit), once each; the pack's directory under knowledge/ (V11) |
| the pack in the archive | matched by publish.include; manifest.json, wiki/index.md and wiki/log.md present; nothing but those, wiki/ and sources/ (V11) |
the pack's manifest.json |
schema knowledge/v1, names the pack, and its sha256, bytes and pages are the files beside it — the check the CLI's pack loader makes before it uses a pack, so edit a pack with agentry knowledge add, never by hand (V11) |
| the instruction file | not under knowledge/: it is the release's own, judged against its contract (V11) |
| size | at most 10 MB a pack, manifest.json included, and 2,000 files; a pack's files are outside the release's own quotas — the file count, the per-file sizes, the 1 MiB text total and the archive total — so the archive's cap is raised by what it ships (V8). An upload through the API (agentry publish) is at most 16 MiB compressed, the edge's request cap; a larger release is published by pushing its tag, which has no such cap |
knowledge/ |
holds shipped packs only: a file there outside a pack ships names is refused (V11) |
accepts.prompt |
one plain line: no control or format characters, not blank |
| schema | v2 only; a v1 manifest carrying knowledge is refused (V1) |
The contract. The names a release ships are part of the capability
contract a person consents to: the version's capabilities carry them as
knowledge, and a release that ships a pack the installed one did not
asks for consent again ("ships knowledge: aws-docs"), as any widening
does. A pack's contents changing under the same name does not re-ask; the
scan reads every release's packs. What accepts asks for never widens
the contract: attaching a person's own pack is the grant, as handing over
inputs is.
The scan. The registry scans each shipped pack as part of the
version's scan, with the reading it gives a pack it stores
(knowledge.md): the secrets, hidden-content,
signature and URL stages over every page and source, then AI Review over
the pack's sources/ and then its wiki/, in chunks no larger than the
judge reads. Its findings are the release's (scanner SK/…, file paths
under knowledge/<name>/) and the gate decides on them as on any other; a
pack the scan could not read in full sends the version to review. The
release's own stages, its own AI Review included, read the release's own
files and not a pack's text; a pack's other files (an image, a source's
original) are read by the release's stages as anywhere in the archive,
and a text file in a pack that is not UTF-8 is a finding that sends the
version to review. The report's knowledge section names, for each pack,
the stages that read it, what they read, the judge and its model, and the
chunks of each half, or "AI Review not configured". The attestation
covers the archive digest, so a shipped pack is signed with the rest of
the release, and a client that verifies the release verifies the pack
with it.
A CLI or desktop older than the core release that added the block refuses a manifest carrying one as an unknown field; use it once your tools are current.
inputs: what files it takes, and how it reads them¶
The files a person hands a run reach the agent through the input tools
(list_inputs, read_input, search_inputs), which offer every input in
two views: text, the text a decoder made of it (a PDF's or an office
document's text, an image's recognised text, HTML as Markdown), and
original, the file as uploaded (a binary file in base64 pages). Without
this block a QuickAgent takes any kind of file, and each file keeps its own
default view: the decoder's text where one made it, and anything else
(code, markup, HTML, configuration, data) as uploaded. That is how every release published
before the block reads, and it stays so. The block lets the author say
what to bring and which view the agent reads first:
inputs:
kinds: [code, data] # documents | code | data | images, or any alone; absent: any
view: original # text | original | both; absent: both
| Kind | Means |
|---|---|
documents |
prose to read: PDFs, office documents, e-books, rich text |
code |
a codebase or its files: source, markup, build and configuration files, a repository (loose or as an archive) |
data |
structured data: CSV, JSON, YAML, spreadsheets, logs |
images |
photographs, scans, screenshots, diagrams |
any |
no particular kind; declared alone |
| View | The agent reads an input by default |
|---|---|
text |
as its decoded text, and as uploaded when it has none (a summariser of saved web pages wants their Markdown) |
original |
as uploaded (a reviewer of a site's pages wants the HTML with its scripts); a PDF, an office document or an image is then base64 pages, so pair original with documents or images only when the agent works on the bytes |
both |
each file's own default; the runner reads it exactly as leaving view out, and it tells the run setup and the Drafter that the agent uses both |
| Rule | |
|---|---|
| the block | at least one of kinds and view; no other key (V12) |
kinds |
from the list above, each once, at least one when present; any alone (V12) |
view |
text, original or both (V12) |
| schema | v2 only; a v1 manifest carrying inputs is refused (V1) |
The default is only a default: the agent can still ask for any view an
input has, so declaring text never hides an original. (Some inputs have
one view: a file decoded out of a document inside an archive has only its
text, and a file the malware check found, a refused file and .git
internals have neither.) The kinds are for
the run setup, which asks for them, and for the Drafter, which fills the
block when it writes a QuickAgent. Neither grants anything: attaching a
file is the grant, so the block is outside the capability contract, and a
release that changes only its inputs asks no one to consent again. Write
guide.files to match it.
The runner sets the default view from the block (CLI v0.6.73 or later):
list_inputs marks it, and the System Prompt names it. The Drafter fills
the block from Drafter 1.9.0. A hosted run takes the block; a CLI before
v0.6.73, or a desktop built on one, refuses a manifest carrying it as an
unknown field. The site's and the desktop's run setups show it.
runtime: informational hints¶
| Field | Meaning |
|---|---|
runtime.models |
free-form model hint patterns (e.g. ["claude-*"]). A hint, never a lock: the operator's runner policy chooses the model and owns the API key |
runtime.interfaces |
which coding-agent exports the package supports: claude-skill, agents-md. Independent of mode — an interactive agent still exports as a Claude skill; what it loses in that host is the Agentry session, not its instructions. It does not pick agentry install's default, which is the runner home (below); the exports are asked for by name |
capabilities: the security contract¶
Closed vocabulary. Omitted or empty means a pure-text QuickAgent
(its run-time tools are write_report, the read-only list_inputs,
read_input and search_inputs, its scratch folder's scratch_list,
scratch_read, scratch_write and scratch_search, export_pdf, and
grep and glob over its inputs, its scratch folder and any attached
knowledge, nothing else; the last seven are the runner's grants to every
release but a built-in, not
lines of the contract). The registry scans releases
against these declarations, an install prompts on them (egress/exec need
typed consent; a widened contract re-asks on upgrade), and the runner
turns each line into a concrete control. The same contract covers support
scripts. The scan reads every shell, Python or JavaScript file in the
package, and reports as high a script under exec: none, a network call
to a host outside network.egress, a file write under
filesystem.write: none, an undeclared subprocess or eval, and any
curl … | sh remote execution; an environment variable read outside
secrets is high as well. It matches text without running anything,
and a host only counts inside a network call, so a documentation URL in a
comment is not a finding (see trust, stage S6).
network.egress
| Value | Meaning | Run-time enforcement |
|---|---|---|
none / omitted |
no outbound network | tools container runs with networking disabled; no fetch tool |
[exact.dns.host, …] |
outbound only to these hosts | fetch granted, restricted to the listed hostnames (checked per request and per redirect hop); the gateway pins the tools container to host:443 with per-server proxies and DNS interception |
"*" |
unrestricted egress | refused at run time unless the operator's policy sets allowWildcardEgress: true; the receipt then discloses it. Where the run's docker filter runs, the tools container reaches out from a network of the run's own that only it joins, and its declared commands run there too; elsewhere, from Docker's default network. Under a sandboxed docker.runtime the policy must also name docker.resolvers, or the run is refused at plan time (exit 11). CLI v0.6.122 or later |
filesystem.read / filesystem.write
| Value | Meaning | Run-time enforcement |
|---|---|---|
none / omitted |
no access in that direction | the corresponding tools do not exist |
workspace |
the whole task workspace | read grants fs_read + fs_list; write grants fs_write; the workspace is bind-mounted into the tools container ro/rw accordingly |
{paths: [repo/relative, …]} |
only these paths | per-path bind mounts plus server-side grant checks; a directory entry covers everything under it |
A paths entry the release does not ship is made an empty folder in the
run's own copy of the workspace before the tools start, so a write grant of
out needs no out/ in the package. An entry that names a file is made a
folder too when the file is missing, and a write to that name then fails:
ship a file you grant by name. An entry inside another write entry shares
that entry's mount.
The agent container itself never mounts the workspace. File access goes through the gateway's tools, which is what makes the contract enforceable.
exec
| Value | Meaning | Run-time enforcement |
|---|---|---|
none / omitted |
no subprocesses | no run_command tool; harness built-in shells are never enabled |
{commands: [jq, git, …]} |
only these binaries | run_command restricted to the listed commands via an argv allowlist plus a shim PATH containing only them |
secrets: a list of environment variable names, never values.
The runner resolves each from the operator's secret source before any
container starts (missing ⇒ refuse, exit 11, naming the variable), and
injects values only into the tools container. The fetch tool can send a
declared secret as an auth header without the value entering model
context; values never appear in plans, receipts, or process listings.
optionalSecrets (v2): the declared secrets a run can do without, by
name. Each must already be declared, under capabilities.secrets or a
declared MCP server's secrets, so the list marks a secret optional and
never adds one to the contract; every declared secret it does not list is
required. Moving a secret between optional and required is no change of
capability, and asks for no consent again; a newly declared secret is a
widening either way. That holds because a consent lists every declared
secret by name, optional or not.
capabilities:
secrets: [GITHUB_TOKEN] # private repositories as well as public ones
optionalSecrets: [GITHUB_TOKEN]
A run starts without an optional secret that has no value (agentry-cli
v0.6.66 and later). The runner writes it empty into the gateway's secrets
file, so the runner's own fetch answers that the secret is not set; the
System Prompt tells the QuickAgent which optional secrets it was not given;
and the receipt's capabilities_enforced["secrets.absent"] names them. A
declared MCP server that reads the secret sees its variable set but empty,
and what it does then is its own. A missing required secret still refuses
the run (exit 11), naming only the required ones. A hosted run asks only
for the required secrets (Hosted runs). Write the
instruction for both cases: say what the QuickAgent does without the
optional secret, never that it stops.
A CLI or desktop that pins a core older than this field refuses a manifest that carries it, by name, so a release that marks a secret optional needs agentry-cli v0.6.66 or later to run. The Builder on the site and in the desktop marks each secret Optional or Required, and the Drafter behind Describe it marks each secret it declares, from its 1.10.0 (agentry-cli v0.6.75).
targetHosts (v2): the ids of the release's host intake questions,
at most four. Each is a machine the person names when the run starts,
which the agent may run any command on, as the account the person signs
in with, through the runner's host tools (Target hosts).
It names no host: the answer does. Adding one is a widening, so an upgrade
that adds one asks for consent again.
browser (v2): a headless web browser for the run, with the sites
the person names (url questions), fixed hosts, a scope (site, the
default, or public) and the declared secrets it may type
(The browser's site).
A new browser, site, host or secret, or site to public, is a widening,
so an upgrade that does one asks for consent again.
dataAccess holds semantic categories for review and audit: email,
calendar, files, tickets, code, messages, payments, other.
Operators may catalog extra MCP servers per category in their runner
policy; undeclared categories are never wired in.
mcpServers declares the MCP servers the QuickAgent requires at run
time — explicitly, version-pinned, from the operator's configured
package registries:
capabilities:
mcpServers:
- npm: github-mcp-server-js
version: 0.1.7 # EXACT version — pinned to this release
tools: [list_issues, get_issue] # optional allowlist within the server
secrets: [GITHUB_TOKEN] # env-var NAMES the server needs
env:
GITHUB_PERMISSION: read-only # plain configuration, never a secret
egress: [api.github.com] # the server's own outbound needs
- pypi: mcp-server-fetch
version: 0.6.2
egress: ["*"]
Each entry names exactly one package — npm: or pypi: — and an exact
version: ranges are rejected at validation, because the pin is part of
this release's content and therefore of its archive digest. Installing the
agent installs the servers (from the operator's configured registries —
npm and PyPI endpoints by default, private mirrors configurable), records
the resolved artifact digests, and configures the gateway to load them
for that exact release signature only. A new version of the agent is a
new supply-chain decision: nothing carries over, not the servers, not
their keys. Per-server secrets join the guided profile setup; per-server
egress is that server's own allowlist ("*" is policy-gated, like agent
egress). A server's egress counts as network the release reaches: a
namespace's blockedCapabilities: [network] refuses it, and a host an
update adds that the previous release did not reach is a low
S9/new-egress finding (medium for "*"). An update that keeps a server
at the same pin but adds an egress host, a secret or a tool, or lifts its
tools list, is a widening and asks for consent again, as a new pin
does. Removing the agent removes the server configuration and keys by
default; keeping them is an explicit option, and a kept configuration is
reachable by nothing except a reinstall of the same digest. Full runtime
mechanics: the Runner reference.
env on a server entry is the plain configuration the server reads
from its environment — a mode switch, a directory, a region — as opposed
to a credential. Values are written in the manifest, so they are visible
in the release diff, part of the release's archive digest like the pin,
and shown to reviewers. The rules (a v2 field): at most 16 entries per
server; names on the environment-variable grammar; values single-line
strings of at most 512 characters with no control characters. A name that
is also listed under secrets — the server's or the agent's — is refused:
a value is either configuration in the contract or a secret from the
profile, never both. agent.yaml is scanned like any other archived
file, and the secret scanner also reads the env values themselves, out
of the manifest the registry stores, whatever the YAML's spelling: a key
named like a credential (…_TOKEN, …_SECRET, …_PASSWORD,
…_API_KEY) carrying a value of eight or more characters, or a
token-shaped value under any key, is a medium finding: the default gate
publishes the version with it on the report, and an organization at
threshold low sends it to review. Both are a backstop, not a place to
keep one. In
the example above,
GITHUB_PERMISSION comes from the manifest and GITHUB_TOKEN from the
operator's profile. The runner compiles env into the server's gateway
entry as its plain environment, shows the pairs beside the server's secret
names at install and asks for consent again when a value changes, and the
receipt names the keys. Values are literal: the gateway would expand a
$ followed by a name character, { or a shell parameter character
($NAME, ${NAME}, $$) against the server's environment, secrets
included, and evaluate a value holding both {{ and }} as a template,
so agentry validate and submission refuse those shapes (V5, "declare a
literal value"); a trailing $ or a single brace pair is literal and
passes. The runner refuses the same shapes at plan time, and with them an
empty value and the @@ marker of its own run placeholders.
Declared servers are scanned too. At submission, stage S7 checks each declared package version against the OSV vulnerability database and reports every advisory as a finding, CVE ids and fixed versions included. A Critical advisory rejects the version. A High one goes to review when its CVSS base score is 8.5 or more, or when it carries no score. A High below 8.5, a Moderate and a Low are shown on the report and do not hold it, like a finding of that severity in your own files. Pin a version with a fix rather than asking for an exception; the trust model page describes what the check does and does not prove.
Remote servers and OAuth sign-in (v2). Many MCP servers run at their vendor and sign a person in on the vendor's own consent screen. A QuickAgent declares one by its address, and may give a package server an OAuth sign-in:
capabilities:
mcpServers:
- url: https://mcp.linear.app/mcp # a remote server: exactly one of npm, pypi, url
auth: oauth # oauth | secret | none
scopes: [read] # what consent asks for (auth: oauth only)
tools: [list_issues]
- url: https://api.githubcopilot.com/mcp/
auth: secret # its one secret, sent as a bearer header
secrets: [GITHUB_TOKEN]
- pypi: workspace-mcp # a package server signed in by OAuth
version: 2.0.1
tools: [search_gmail_messages, get_gmail_message_content]
egress: [gmail.googleapis.com]
env:
MCP_SINGLE_USER_MODE: "1"
WORKSPACE_MCP_TOOLS: gmail
WORKSPACE_MCP_READ_ONLY: "true"
USER_GOOGLE_EMAIL: me@agentry.invalid # names the token file below
oauth:
issuer: https://accounts.google.com
scopes: [https://www.googleapis.com/auth/gmail.readonly]
files: # written into the server's own token folder
- path: me@agentry.invalid.json
template: '{"token": "{access_token}", "expiry": "{expiry}", "scopes": ["https://www.googleapis.com/auth/gmail.readonly"], "token_uri": "https://oauth2.googleapis.com/token", "client_id": "agentry", "client_secret": "agentry"}'
env:
WORKSPACE_MCP_CREDENTIALS_DIR: "{dir}"
- A remote server takes an
https://address on a public host name (lowercase, a top-level label that starts with a letter, port 443, no user information, query or fragment, and no space, quote,$or brace; at most 512 characters). Its host counts as network: a namespace'sblockedCapabilities: [network]refuses it, and a new host in an update is a lowS9/new-egressfinding. The address's host is the server's one network destination, so it takes noversion,egress,envoroauth.authis required.auth: secretsends exactly one declared secret as the bearer header,auth: nonesends nothing, andauth: oauthneeds the person's connection. - A connection is the person's OAuth grant to one server, made on the vendor's consent screen the first time a run of that QuickAgent version needs it, and kept for that version's later runs. It belongs to that one version. Another QuickAgent, or another version of this one, makes its own.
- An
oauthblock on a package server names the authorization server (issuer,https://), thescopesconsent asks for, and how the access token reaches the server: files: at most four, each a plain file name in the server's token folder (letters, digits,.,_,-and@, at most 64;@for a server that looks its file up by an account's email, asworkspace-mcpdoes) and a template of at most 2,048 bytes. A template's placeholders are{access_token},{expiry}(RFC 3339, UTC) and{expiry_ms}(Unix milliseconds).env: at most eight variables. An env value's placeholders are{access_token}, and{dir}, the token folder's path, which needs at least one file declared.
{access_token} must appear somewhere. A name may not repeat the
server's env or a secret.
- What a run will receive. Once the runner carries connections
(below), a run receives an access token and nothing else of the
connection: never a refresh token or a client secret. A value a server's
file needs for a client id or secret is a fixed dummy (as agentry
above). The runner writes the files itself, never through the gateway,
so a template's $ and braces are content. The secret scanner reads
every value of the oauth block's templates and env, in any format,
with the placeholders taken out, and reports a token-shaped one
(S2/oauth-token-value, medium), as it reads env. It is a backstop:
a value that does not look like a token passes it.
- Servers that cannot run on an access token alone refuse an oauth
block by name, with an alternative to declare:
| Package | Declare instead |
|---|---|
pypi:mcp-gsuite |
pypi:workspace-mcp |
- At the gate, stage S7 reports each remote server as
S7/remote-server, a low finding naming its address, auth and scopes. There is no package to look up and no code the pipeline reads, and the report says so. A version publishes with it. A publication policy'sblockedMcpServersblocks a remote server by its host (url:<host>, globs allowed, as inurl:*.example.com). - Consent. The capability diff the clients ask consent from
(
DiffCapabilities) calls each of these wider than before: a new remote server or address, a changed auth or issuer, a new scope, a remote server's tool added or its allowlist lifted, a different bearer secret, an added OAuth sign-in, and a changedoauthfile or env. The CLI and the desktop show them once their core pin carries this change.
The CLI runs a remote server with auth: none or auth: secret through
the runner image's bridge, auth: oauth on the person's connection, and a
package server's oauth block
(see the runner reference): the access token is
written into the files the block declares, in a folder of the server's own,
or into its env, and renewed before it expires while a local run lasts
where the server reads it from a file (an env token is fixed for the run,
so a server that reads its token from env alone stops working once the
token expires, about an hour for Google). A file's client id or secret, if
the server's format has one, is a fixed placeholder the author writes (as
agentry above), never a real value: the runner substitutes nothing but
the placeholders. A run in Agentry uses the connection Agentry keeps for
the account and the release
(Hosted runs). A CLI or desktop older
than its core pin refuses such a manifest by its unknown field.
dependencies¶
dependencies.agents: [{ref: ns/name, version: "^1.2"}] is informational:
recorded and displayed, not auto-installed.
publish¶
| Field | Meaning |
|---|---|
publish.include |
required; the release archive allowlist of glob patterns (path.Match segments; a whole segment ** matches any depth). Nothing outside it ships, and a v2 manifest must match agent.yaml itself: the runner reads the manifest from the installed archive |
publish.channel |
stable (default) or beta |
Installing on this machine¶
agentry install ns/name resolves the release, verifies its sha256
digest and its DSSE attestation against the keys pinned for the registry
(unsigned requires --allow-unsigned; a version admitted without a scan
requires --allow-unscanned), shows the declared capability contract
diffed against the version installed here — declared egress or exec
needs typed consent on a first install, and so does anything widened
since (--yes for CI; off a terminal, without it, the install refuses)
— and lays the release down in the runner home:
~/.agentry/runner/agents/<ns>/<name>/<version>-<digest12>/, the
directory agentry run reads, so a run after the install fetches
nothing and the consent given here covers it. Inside it, under
export/, lands the shape a coding agent reads, in the style the
operator policy implies: a Claude skill when the policy names Claude
Code (harness.claude), AGENTS.md when it names Codex, else a Claude
skill for an Anthropic key and AGENTS.md for any other provider. A newer
version replaces the older; one record per QuickAgent in the install
state (agentry list; --check asks the registry whether a newer
version exists). --dir is for the two exports; the runner home is one
(AGENTRY_RUNNER_HOME). agentry uninstall ns/name removes the
directory, the record and, unless --keep-mcp, the MCP profile.
Installing into an existing coding agent instead is --target or
--provider, below.
This record is this machine's. The registry keeps a separate one per
account — the release a signed-in person accepted on the web and the
contract they consented to, GET /user/installs in the
API conventions — and the two are
never synced: installing here records nothing there, and the reverse.
What a QuickAgent gets at run time¶
agentry run verifies a release it has not installed exactly like
agentry install (sha256 digest + DSSE attestation against pinned keys;
unsigned requires --allow-unsigned), asks the registry whether any declared MCP server
version carries a known high or critical vulnerability (refusing if so,
unless the operator overrides it), compiles manifest + operator policy into
a deterministic run plan, then executes one container tree. That is an
autonomous run; an interactive session starts from the same plan
and the same enforcement and adds only a conversation and a read-only
file drop (see Modes). The isolated
agent container sits on an internal-only network; its only reachable
endpoints are the MCP gateway and an allowlisting egress proxy for the
model API. A per-run Docker MCP Gateway fronts the companion tools server.
| Manifest declaration | Tool(s) granted |
|---|---|
| (always) | write_report: write files into the run's output folder, and mark the one the person asked for as the run's deliverable |
| (always) | list_inputs, read_input, search_inputs: the run's provided input files, read-only |
| (always, but for the built-ins) | scratch_list, scratch_read, scratch_write, scratch_search: the run's scratch folder at /scratch, private to the run and deleted at its end |
| (always, but for the built-ins) | export_pdf: a Markdown report as a PDF in reports/, in the form the agent picks (report, the default, letter or memo) and laid out in the person's report theme |
| (always, but for the built-ins) | grep, glob: search file contents by regular expression and list files by path pattern with ripgrep, one area a call, only what the run's other tools read: /inputs, /knowledge/<pack>, /scratch, and /workspace only with filesystem.read. ripgrep runs with an empty environment, as a user of its own when the tools server is root, a fixed argument list that never starts another program, and caps on memory and time; where the machine has Landlock it also reads only the searched area and writes nothing, and at Landlock ABI 4 or later opens no TCP connection |
filesystem.read |
fs_read, fs_list |
filesystem.write |
fs_write |
network.egress hosts |
fetch (with optional declared-secret auth) |
exec.commands |
run_command |
browser |
the browser tools (browser_navigate, browser_click, …), from the browser server |
targetHosts |
the host tools (host_run, host_read, …), from the host link |
mcpServers |
each declared server's own tools |
A tool whose line the manifest does not declare is absent from the run, not present and refusing, so an instruction that calls for one describes a run that cannot happen. The registry reads the tool names an instruction calls for against these lines at publication, and a release that names a tool it is not granted goes to a person rather than publishing (stage S6, since ruleset 0.4.10; trust); the check of a finished draft lists the same tools before Submit (model input).
The runner puts the System Prompt before every instruction. Its fixed
head says the run is in a locked sandbox, puts attached knowledge first, and
keeps the model on the instruction's task. Its body is composed from what
the manifest grants, and never states a grant the manifest does not make.
It covers the tools (only from the gateway), the network, the workspace at
/workspace, the scratch folder at /scratch, searching files with
grep and glob, the commands, the secrets
by name, and the inputs under /inputs. It explains how to read an
attached knowledge pack, that reports go through write_report, that the
file the person asked for is marked as the run's deliverable, when to also
export one as a PDF with export_pdf, and that the final message is the
run's result.
So an instruction need not repeat any of this. The text is in the run
plan's hash, and the runner reference shows it word
for word for a sample QuickAgent.
Input files. Files and artifacts from outside the container reach a
run through agentry run --file PATH (repeatable; files or directories),
a file intake answer, a file added to a session, or the uploads of a
hosted run. They are copied into a per-run folder mounted read-only at
/inputs on the tool plane — a channel like stdin, operator-controlled per
run, so it needs no manifest capability. Job output and receipts stay on
the separate /out volume.
Before the agent starts, the runner checks every file for malware and turns it into text where it can, in a container with no network (running with files). So a QuickAgent that takes files can count on these:
- Common documents work: PDF, Word (DOCX and DOC), OpenDocument, EPUB, RTF, spreadsheets (XLSX and ODS as Markdown tables, XLS as text), PowerPoint and OpenDocument slides, Outlook and other mail (MSG, EML, mbox), and images, whose text is read by OCR. Code, markup and data (source, HTML, XML, CSV, JSON, YAML, logs) arrive as uploaded.
- Archives work: zip, tar, 7z, rar and gzip, bzip2, xz or zstd
streams are unpacked, and each file inside is read by its path,
bundle.zip/folder/file.docx, up to three archives deep, 2,000 files and 512 MiB unpacked. - Every file is checked: by ClamAV, and the risky types (executables, scripts, macro and legacy Office, RTF, PDF and a few more) by their SHA-256 on Hybrid Analysis, never by upload. A file either check refuses stops the run before the agent starts (exit 11), naming it; on Agentry that costs nothing. On Agentry, AI Review also reads every input's text, and one it flags refuses the run for one credit.
- The agent never needs
execto read them:list_inputs,read_inputandsearch_inputsserve each file's text and its original, and theinputsblock chooses which view is read by default.
A hosted run takes at most 20 files of at most 10 MB each
(hosted runs). Write guide.files to say which
files the QuickAgent wants, and the run setup shows it.
Structured output. Ship an output.schema.json at the archive root
(listed in publish.include) and the runner embeds the schema in the
instruction and requires the final message to parse as JSON. Exit 17
otherwise, with the raw output preserved in the receipts.
Task input and composition. --input FILE, else piped stdin, else an
empty task. Runs compose: agentry run a … | agentry run b ….
Budgets and exit codes. The operator policy sets the default timeout,
tool-call budget and cost ceiling; --timeout / --max-turns /
--max-cost override per run (a run is killed once the harness reports
more than the cost ceiling, 0 = unlimited). Exit codes: 0 ok · 1
other CLI error · 2 usage · 10 verify failed · 11 refused by policy /
missing secret · 12 not compilable/runnable · 13 gateway or docker
environment · 14 the QuickAgent failed · 15 timeout · 16 budget ·
17 output schema.
The output folder (--out, default
./agentry-out/<ns>-<name>-<runid>/) holds reports/…, result.json
(the machine-readable envelope: enforcement mechanisms applied, digest,
attestation state, usage, run-plan hash, and the run's
deliverables), and receipts/ (event stream,
gateway call log, the exact run plan).
Deliverables¶
The files a run was for are its deliverables. A QuickAgent whose result
is a file marks it: write_report takes deliverable: true and a title,
one line of plain text of at most 120 characters, and so does export_pdf
for a PDF. The runner then lists the file in the envelope by its SHA-256,
and the person's tools can show it first and copy it to a folder of their
own (the runner reference).
The System Prompt already tells the agent to mark the file the person asked
for and to leave notes and drafts unmarked, so an instruction need not
repeat it. It helps to say which file is the result, in the ## Output
section of the instruction: "write the summary with write_report as
summary.md, titled 'Weekly summary'". When a run marks nothing, the runner
lists the PDFs it exported, else the files in reports/, else the final
message, which it writes to reports/deliverable.md. A QuickAgent that
returns only its answer needs no marking. A QuickAgent with an output schema
returns structured data, which is the result in the envelope and is not
written to a file.
A publisher chooses nothing about where a deliverable goes. The person
chooses a folder (agentry run --deliver-to, or deliver.folder in their
config), and a release names none in its manifest. Marking needs agentry-cli
v0.6.138 or later: an older CLI's export_pdf refuses the two arguments
(its write_report ignores them).
Operator policy (~/.agentry/runner/policy.yaml, created on first
use) pins the gateway/harness images, names the model endpoint and its
API-key variable, sets the egress-proxy allowlist and budgets, chooses
the secret source, and gates wildcard egress.
Installing into an existing coding agent¶
The same package installs into a host coding agent with that application's MCP servers and built-in tools standing in. Nothing is bundled either way, and these users never go through the docker gateway path. Each major agent application has its own install entry point:
agentry install ns/name --provider claude # Claude Code
agentry install ns/name --provider codex # Codex
agentry install ns/name --provider openclaw # OpenClaw
--provider picks the right target below (the web Downloads page lists
these flows); the explicit --target forms remain, and either names the
export — with neither, agentry install goes to the runner home:
| Target | What lands | Contract mapping |
|---|---|---|
claude-skill |
~/.claude/skills/<name>/ with SKILL.md frontmatter |
capabilities compile to allowed-tools: Read/Grep/Glob ⇐ read, Write/Edit ⇐ write, Bash(cmd:*) ⇐ exec, WebFetch(domain:host) ⇐ egress |
agents-md |
./agents/<name>/ + a section in ./AGENTS.md |
the section records the same contract as a Tools note |
Enforcement in embedded mode is the host's. The trust model page explains what each layer does and does not prove.
Publishing¶
Three doors, one pipeline. agentry publish submits a workspace from the
command line; the hosted site's QuickAgent Builder (/build, on a
registry with the site in front) authors one in the browser — anyone can
download the packed archive for local use, and signed-in users can submit
it; and the desktop app's Build tab does the same from a form. Repos
hosted on the registry can also publish by pushing a release tag (the
agent's auto-publish-tags setting). Whichever door, the submission is
identical server-side: manifest validation, content scanning against the
declared capabilities, human review where findings demand it, and DSSE
signing. A publish to a public namespace needs a second factor on the
account, asserted by the identity provider you sign in with, when the
registry has that on — /meta reports it as features.publicPublish2FA.
Published releases are immutable and content-addressed. The language rule
applies to QuickAgents as it does to everything here: a release is
scanned, with N findings, signed, never "safe". Declare the least you
need: the contract is what the scan, the install's consent and the runner
all work from.
agentry publish and a tag push both need the QuickAgent's record,
which the CLI never creates: a first publish of a name with no record is
answered 404. Create it at /new on the site, which asks for the
namespace, the name, the run modes and the visibility and sets up the git
repository a tag is pushed to, or with POST /agents. The Builder's and
the desktop's Submit create the record themselves.
Before submitting, agentry review . runs the built-in Examiner on your
own computer: the registry's rules, AI Review's questions and a model's
reading of the files, with the outcome the gate would be expected to
reach. It submits nothing, and the registry's scan at submission is the
one that counts.
Every door checks the same quotas (V8): at most 2,000 files and 10 MiB in
the archive, 2 MiB per file (5 MiB per image), and 1 MiB of text files in
total — the prose the scanners read in full, so an instruction set cannot
grow past what a reviewer can read by being split across files.
agentry validate reports the same limits offline.