openstation

Start Here

Architecture

Follow a message from a connector through the harness to the workspace.

What happens between a message arriving and a reply going out — the parts that change how you configure things. The full design lives in internal/architecture/00-overview.md.

The path a message takes

Slack · Telegram · Email · REPL
        │
        ▼
Connector      normalizes the message into a common shape
        │
        ▼
Bridge         who is this? · are they allowed? · which agent · which session
        │
        ▼
TurnRunner     concurrency cap + one turn at a time per conversation
        │
        ▼
Executor       turns that into a Claude Code invocation
        │
        ▼
Claude Code    reads the charter, skills, and tools; does the work

The one thing to internalize

The platform resolves pointers. Claude Code materializes context.

The Bridge decides which session to resume, which agent answers, and what that agent is permitted to do. It does not assemble a prompt, gather files, or build context — Claude Code does that at run time from the workspace it's pointed at.

This is why nearly everything you write to shape an agent's behavior is a Claude-native file:

You want to change You edit
what the agent does and how it talks .claude/agents/<name>.md
what it's allowed to touch .claude/settings.<label>.json
a reusable procedure it should follow .claude/skills/<name>/SKILL.md
which agents exist, and their identity/executor/settings binding .openstation/openstation.yaml

The manifest references those files. It never restates a prompt, model, or tool list — the loader rejects the attempt rather than letting two sources of truth drift apart.

Turns are synchronous

One message in, one reply out, on the request path. There's no queue you can inspect and no job id to poll: the connector waits for the turn and delivers whatever comes back.

Two consequences you will actually hit:

A concurrency cap. Three turns run at once by default, and one permit is reserved for interactive traffic so background work (triggers, scheduled jobs) can never starve a live conversation. Over the cap a turn waits its turn (FIFO, up to 32 waiting) and then runs; only a caller arriving once that queue is full is told:

I'm at capacity right now — please try again in a minute.

One turn at a time per conversation — and a second message is refused, not queued. If a message arrives for a conversation whose turn is still running, the reply is:

Still working on your previous message — please wait.

Nothing is buffered. The user has to send it again. (Design doc 03 describes this as serializing, which reads as the second one waits; the implementation rejects it.)

What Claude Code brings

Session continuity (--resume), the workspace charter, skills discovery, tool execution, and permission enforcement all belong to Claude Code. OpenStation's job is to point it at the right workspace with the right settings file and get out of the way.

That's also where the safety boundary lives: the permission gate is the settings file Claude loads, not a filter the platform applies. See permissions.

Where the pieces live

Concern Page
which file holds what agents and profiles
where the agent's work goes workspaces and git
how a conversation stays continuous sessions and turns
what's allowed, and who enforces it permissions
View Markdown source on GitHub ↗