Skip to content

Security

The threat model, hardening measures, and ongoing obligations for the Hall of Automata.


Threat model

The system's security rests on two assumptions:

  1. Org admins are trusted. They have access to org secrets. If an admin is hostile, the key custody model collapses. This is an organizational assumption, not a technical one.
  2. Workflow source is protected. A malicious modification to .github/workflows/ could bypass the team membership check or exfiltrate secrets. Protection is enforced via branch rules and CODEOWNERS.

Threats outside this model — compromised GitHub infrastructure, Anthropic-side breaches — are not in scope here.


Protecting workflow source

The most important protection in the system. A workflow that can be modified by anyone can do anything, including printing secrets to logs.

Branch protection on main: - Require pull request reviews before merging - Require at least 1 approval from a code owner - Dismiss stale reviews when new commits are pushed - Do not allow bypass by admins (ideally)

CODEOWNERS:

# .github/CODEOWNERS
.github/workflows/  @MockaSort-Studio/admins

Any change to workflow files requires approval from the admins team. This is the primary guard against workflow tampering.


Secret hygiene

Mask the OAuth token in every workflow:

- name: Mask invoker token
  run: echo "::add-mask::${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}"

This must be the first step after authorization passes, before any step that uses the token. Once masked, the value is redacted from all subsequent log output.

Never print secrets explicitly. No echo $SECRET, no debug steps that dump environment variables. If ACTIONS_STEP_DEBUG is ever enabled, disable it before running any invocation job.

Minimal workflow permissions:

permissions:
  issues: write          # post result comments
  pull-requests: write   # open PRs, post review responses
  contents: write        # create branches, push commits

Nothing else. The GITHUB_TOKEN should not have write access to anything it does not need.


Team membership check

The Hall App holds Members: read org permission. No separate ORG_READ_TOKEN PAT is required. Authorization runs against the App's own installation token.

Authorization is fail-closed: any API error (network, token issue, user not found) blocks the invocation. See architecture/permissions-model.md for the full flow.


Concurrency

Concurrent invocations of the same automaton on the same issue are serialized, not raced:

concurrency:
  group: hamlet-${{ github.event.issue.number }}
  cancel-in-progress: false

cancel-in-progress: false queues rather than cancels. Both invocations run, in order. If cancellation is preferred (last writer wins), set to true.


Invocation timeout

All workflows set a hard timeout:

timeout-minutes: 30

A stuck or runaway invocation does not run indefinitely. Adjust per automaton if tasks are expected to take longer, but always set an explicit limit.


Audit trail

Every invocation leaves two records: - Actions log: timestamp, trigger, sender, job result - Issue comment: automaton response or unauthorized notice

These are not deletable by regular org members. They provide a complete history of who invoked what and when.


What this does not protect against

  • A compromised invoker account with direct org admin rights
  • GitHub infrastructure compromise
  • Anthropic-side data handling (governed by Anthropic's terms)
  • Repos where the Hall App is not installed — the relay will not forward webhook events for those repos