openstation

Core Concepts

Agents

Configure agent charters, profiles, executors, and settings.

An agent in OpenStation is spread across a few small files on purpose. Knowing which file holds what saves you from the most common mistake: putting behavior in the manifest, where it will be rejected.

Which file holds what

File Owner Holds
.openstation/openstation.yaml OpenStation which agents exist; each one's identity, executor, settings label, channel claim
.claude/agents/<name>.md Claude Code the charter: system prompt, model, tool list
.claude/settings.<label>.json Claude Code the enforced permission gate
.openstation/profiles/<name>.md OpenStation an explicitly selected behavioral addendum layered onto the charter
.claude/skills/<name>/SKILL.md Claude Code a reusable procedure Claude discovers on its own

The manifest references the Claude-native files by name; it never restates their contents. This isn't a style preference — the schema is strict, so a model: or tools: key in openstation.yaml fails at load:

agents.a: Unrecognized key(s) in object: 'model'

The point is that there's exactly one source of truth for behavior and exactly one for enforcement, and neither can silently drift from a copy.

The charter

.claude/agents/<name>.md is an ordinary Claude Code agent file — frontmatter plus a system prompt:

---
name: notes
description: Keeps a durable, searchable record of decisions and open questions.
tools: Read, Glob, Grep, Skill, Edit, Bash
---
You keep this workspace's written record.

When someone tells you something worth remembering — a decision, a problem, an open
question — write it down as a note under `okf/issues/` using the `note` skill…

Watch the tools: dialect. In a charter it's a comma-separated string — Claude's own format. In a profile file it's a YAML array. They look similar and are not interchangeable:

tools: Read, Glob, Grep          # .claude/agents/<name>.md — comma string
tools: ["Read", "Glob", "Grep"]  # .openstation/profiles/<role>.md — YAML array

Note also that tools: in the charter is not the security boundary. The settings file is. See permissions.

Profiles

A profile is a behavioral addendum layered on top of the charter. It lives at .openstation/profiles/<name>.md and can be selected for a person, a space, or an agent:

---
tools: ["Read", "Glob", "Grep", "Skill", "Edit", "Bash"]
---
You are talking to a teammate. Be brief. Prefer filing a note over a long reply — a
sentence saying what you filed and where beats three paragraphs they have to re-read.

Frontmatter takes tools (required, at least one pattern) and an optional model. The body is the addendum.

Selection is explicit: the person's profile in people.yaml, then the matching space's profile, then the agent's profile. If the agent declares none, the CLI uses its startup role (--as, default member) as the default profile name. A caller's admission role does not automatically change the profile on each message.

Missing named profiles are refused, not downgraded. Person roles named in a channel's roles: list also need corresponding profile files at load time; agent-only roles that compose permission gates do not. See roles and people for the distinction.

Profiles carry no authority. claudeSettings resolves independently from person to space to agent; putting it in a profile is a load error.

Which agent answers

Selection reads the manifest: an exact channels: match wins, otherwise the agent declared default: true, otherwise nothing (which is a boot failure).

It resolves once, at startup. dev selects with the channel repl; serve passes no channel at all and therefore always lands on the default agent. One process serves one agent for its whole lifetime, no matter how many agents the manifest declares or what channels they claim.

So channels: today is a claim — validated for conflicts at load, stored, and not consulted per message. To run two agents, run two processes with different -a values. Per-message routing is tracked in internal/roadmap.md under "Known gaps to v1".

Adding an agent

bun packages/cli/src/index.ts my-space agents add reviewer --channel review

Config only — no platform code changes. It writes the manifest entry plus the Claude-native files that entry references, which is the whole point of the overlay: a new agent is a declaration, not a deployment.

Which conversations that agent answers in is a separate question: see spaces and channels. Handing work to a second agent mid-turn is subagents.

View Markdown source on GitHub ↗