openstation

Configure

Roles and people

Separate caller admission, agent authority, and behavior.

OpenStation separates identity, admission, behavior, and tool authority. Start by deciding whether you are defining a role for a person entering a conversation or for an agent receiving a reusable set of permission rules.

Four separate decisions

Decision Configured in Effect
Who is speaking? people.yaml, members[].contacts Maps a channel handle to a stable person
May they start a turn here? The person's role and channels[].roles Admits or refuses the caller
How should the agent behave? Explicit profile on the person, space, or agent Selects the behavioral addendum
What tools may the agent use? claudeSettings and the settings file it names Selects the enforced gate

Both profile and claudeSettings resolve from person to space to agent, independently. The CLI uses its startup role (--as, default member) as the agent's profile fallback when the agent has no explicit profile. A caller's admission role does not automatically select a different profile or settings file for each message.

Define roles

The optional .openstation/roles.yaml roster declares which roles exist and their scope:

roles:
  member:
    scope: person
    description: A teammate admitted to shared conversations.
  admin:
    scope: person
    description: An operator admitted to the administration space.
  private-state:
    scope: agent
    required: true
    description: Keep platform state and credentials out of agent reads.
    deny:
      - Read(var/**)
      - Read(.env)
      - Read(**/.env)
      - Read(.env.keys)
      - Read(**/.env.keys)
      - Read(**/secrets/**)
  notes-writer:
    scope: agent
    description: Read workspace context and maintain the notes bundle.
    allow:
      - Read
      - Glob
      - Grep
      - Skill
      - Edit(okf/**)

Use scope: both only when one named role is intentionally valid for both consumers. Scope is required. Agent roles compose gate files; person roles are usable at admission sites.

Assign people

Map service handles in .openstation/people.yaml. These handles are examples; replace them with the IDs reported by your connector.

members:
  - id: alice
    role: admin
    contacts:
      slack: U01234567
  - id: sam
    role: member
    contacts:
      telegram: "123456789"

With a roster present, omitting defaultRole refuses unknown contacts. With no people roster, identity is passthrough and callers receive the process's startup role. See the people.yaml reference before choosing that behavior.

members add can create the roster and a missing profile. It initially adds no contact handles and creates a new roster with defaultRole: member; review that default when access should be limited to explicitly listed people.

Configure the agent and its spaces

Merge this agent declaration into .openstation/openstation.yaml:

agents:
  notes:
    identity: agent:notes
    executor: claude-cli
    default: true
    profile: member
    claudeSettings: notes
    roles: [private-state, notes-writer]
    channels:
      - connector: slack
        type: dm
        roles: [admin]
        profile: admin

The example changes behavior in the Slack DM while retaining the notes agent's gate. To grant different authority there, explicitly name a reviewed claudeSettings file on the space. Person-level overrides follow that person into every space, so reserve them for behavior or authority that should travel with them.

Provide .claude/agents/notes.md and the referenced profiles. Person roles admitted by a channel need .openstation/profiles/<role>.md at load time; here that means admin.md, while member.md is also required as the agent's named profile. Agent-only permission roles need no profile file. A profile's tools: is a YAML array and describes an advisory ceiling; it is not the permission boundary.

Compile and check gates

When adding a roles roster to an existing workspace, migrate the current allow rules, deny rules, and supported role hooks before writing gates. Review all generated changes.

bun packages/cli/src/index.ts my-space gates write
bun packages/cli/src/index.ts my-space gates check

gates write generates .claude/settings.<label>.json from the agent's roles and inline allow/deny lists. gates check detects drift. dev and serve refuse drift at boot and do not silently regenerate files.

Role rules are unioned, and denies win over allows. Adding an allow role can grant new capabilities that are not denied; it cannot override an existing deny. A required agent role must be held or explicitly exempted with a nonempty reason.

The roles reference covers scopes, hooks, exemptions, and invalid patterns. Read permissions for enforcement details and executor differences.

View Markdown source on GitHub ↗