Skip to content

The agent folder

An agent is a folder. This page shows its layout and walks through writing a small agent from scratch.

Layout

my-agent/
├── vessel.toml          # required: the manifest
├── prompt.md            # required: the briefing
├── tools/               # optional: declared tools
│   ├── lookup.toml
│   └── lookup.py
└── sandbox.Dockerfile   # optional: extra software for the sandbox
File Required What it does
vessel.toml yes Names the agent, picks its model, lists its tools, and says what data and limits its sandbox needs. See vessel.toml.
prompt.md yes The agent's briefing, used as the start of its system prompt. See Prompts.
Declared tool files no One TOML file per tool, listed in [tools] declared. Paths are relative to the folder and must stay inside it. See Declared tools.
A sandbox Dockerfile no Named by [sandbox] dockerfile, built FROM astralvessel. See Extra software.

The manifest lists everything the agent has. A tool that is not listed does not exist for the model.

The sandbox mounts the folder read-only at /agent, and tool calls may read it, so keep secrets out of it.

Write an agent

This agent answers questions about a folder of Markdown notes.

  1. Create the folder and the briefing:

    mkdir -p agents/notes
    cat > agents/notes/prompt.md <<'EOF'
    You answer questions from the notes in /data. Search before you answer, quote the
    lines you rely on with their file names, and say plainly when the notes say nothing.
    EOF
    
  2. Write the manifest, agents/notes/vessel.toml:

    [agent]
    name = "notes"
    description = "Answers questions from a folder of notes."
    examples = ["What did we decide about the release date?"]
    
    [model]
    model = "your-model-name"
    
    [tools]
    builtin = ["read", "grep", "glob", "list"]
    
    [sandbox]
    workdir = "/data"
    mounts = [{ source = "notes", target = "/data" }]
    

    The mount says the agent needs data at /data. source is only a label here; the deployment decides what is actually mounted.

  3. Grant it in deployment.toml:

    [egress]
    upstream_url = "https://api.example.com/v1"
    
    [[agents]]
    id = "notes"
    package = "agents/notes"
    [agents.sandbox]
    mounts = [{ target = "/data", source = "/srv/notes" }]
    
    [[apps]]
    name = "web"
    key_sha256 = "env:WEB_APP_KEY_SHA256"
    agents = ["notes"]
    

    package is relative to the deployment file. The grant's target must match the manifest's mount target.

  4. Deploy it:

    astralvessel deploy deployment.toml
    docker compose -f compose.generated.yml --env-file .env up -d --build
    

    deploy refuses a deployment whose grants do not match what the agent declares, so a missing or extra mount or service fails here, not at run time.

One folder, several agents

A deployment can list the same folder under several agent ids. Each id gets its own sandbox, card and A2A address. Use prompt_append to give each one extra briefing files from the folder:

[[agents]]
id = "notes"
package = "agents/notes"

[[agents]]
id = "notes-brief"
package = "agents/notes"
prompt_append = ["brief.md"]

See Prompts for how the system prompt is put together.

Check a folder before deploying

load_package parses and validates a folder the same way the vessel does:

python -c 'from astralvessel.manifest import load_package; p = load_package("agents/notes"); print(p.tool_scopes())'

It raises an error naming the first problem: an unknown setting, an unknown built-in tool, a declared tool outside the folder, or two tools with the same name.