Skip to content
TilloTech Docs

The Harbourmaster shared-action trust boundary

Harbourmaster runs repository code analysis inside a GitHub Actions job that can request an AWS OIDC token and publish pull request feedback. Its workflow design therefore treats executable references, event context, and repository checkout state as parts of one trust boundary.

The design was exercised while adding Harbourmaster to TilloTech/go pull request 76. The review exposed several principles that apply to every consuming repository.

flowchart LR
    Event["pull_request event"] --> Guard{"Same repository and not draft?"}
    Guard -->|"No"| Skip["Do not run a review"]
    Guard -->|"Yes"| Request{"Label absent or explicitly re-added?"}
    Request -->|"No"| Skip
    Request -->|"Yes"| Label["Apply harbourmaster-review label"]
    Label --> Checkout["Checkout exact reviewed head SHA"]

    Action["Harbourmaster action from EOS main"] --> Runner
    Checkout --> Runner["Trusted Harbourmaster runner"]
    Runner -->|"Scoped patch workspace"| OpenCode["OpenCode agents"]
    Runner -->|"OIDC role"| Bedrock["Amazon Bedrock"]
    Runner -->|"Octokit reads and verification"| GitHub["GitHub pull request"]

    OpenCode -->|"Trusted local publication plugin"| GitHub
    OpenCode -.->|"Raw process logs"| Runner
    Bedrock -.->|"Model responses"| Runner

The primary harbourmaster-review-editor session acts as the coordinator. It invokes the specialist and verifier sessions, and it is the only session that can publish to GitHub through one local publication tool configured by the trusted action. The tool validates a runner-authored manifest, publishes the review, applies prior-thread dispositions, and writes a private receipt. OpenCode starts in an isolated diff directory with the trusted bundled configuration. The code-quality reviewer alone also has read-only access to the whole canonical checkout. Other specialists and the verifier remain limited to the staged diff. Consumer OpenCode configuration remains untrusted review context and cannot execute. For each editor attempt, the runner passes the resolved GitHub token only in the OpenCode process environment. The trusted publication plugin uses that process-level token. Specialist and verifier sessions have no publication or GitHub tools and receive no token in their session inputs. The runner verifies a new marked review on the exact head SHA after OpenCode exits.

Runtime references follow repository role

Consumer repositories invoke TilloTech/eos/apps/harbourmaster@main. They therefore receive only Harbourmaster changes that have merged to the EOS main branch. The EOS repository invokes its local ./apps/harbourmaster action from the pull request head so changes to Harbourmaster can be tested before merge.

The checkout action remains pinned separately because it prepares the reviewed workspace inside the OIDC-enabled job. EOS owns the transitive action dependencies inside Harbourmaster and reviews their pins before changing them.

Event and checkout identity must agree

The pull_request event supplies context to a workflow that owns both label creation and review execution. A job guard excludes drafts and forks before checkout or credentials are available. Automatic events run only while harbourmaster-review is absent; explicitly re-adding that label requests another review. When the label is already present, the job emits a warning and fails before checkout so a required Harbourmaster check blocks the unreviewed head. A human must remove and re-add the label to request re-review; automation must not do so. The job receives the pull request number and exact head SHA directly from that guarded event.

Harbourmaster verifies the checked-out Git HEAD against head_sha before starting reviewers. This couples the review result to the code that triggered the run and prevents a stale or implicit branch reference from changing the review target. Consumer repositories load the Harbourmaster action from EOS main. EOS self-reviews load the local action from the exact pull request head so runtime changes are exercised before merge.

pull_request_target would move workflow execution into the privileged base repository context. Combining that event with checkout of untrusted head code would cross the intended trust boundary, so Harbourmaster consumer workflows do not use it.

Security policy needs executable checks

The fork and draft guards, exact-head checkout, least-privilege permissions, disabled credential persistence, and runtime reference policy are security controls, not formatting conventions. Documentation records their purpose, but tests and workflow policy checks detect accidental removal or inversion.

EOS tests the shared action's packaging and its own workflow structure. A consuming repository remains responsible for checking its workflow, including the event, fork and draft guard, permissions, checkout SHA, and persist-credentials: false setting.

Operator configuration belongs in operator documentation

An implementation specification can explain why an OIDC role is required, but it is not the setup interface for repository operators. The required HARBOURMASTER_AWS_ROLE_TO_ASSUME variable must be discoverable beside the workflow setup, including where it is configured and what value it accepts.

This separation keeps intent records stable while allowing operational guidance to evolve with the supported action contract.

Portable records preserve traceability

Repository-relative paths and commit-anchored GitHub links remain resolvable by other contributors and CI. Machine-specific absolute paths only describe one author's checkout and weaken the connection between an implementation record and the files it references.

Cross-repository records identify both the repository and relative path, or use a permalink at the reviewed commit. This keeps the evidence behind trust decisions inspectable after the original workspace no longer exists.

EOS self-review is an invariant

Consumer repositories must load the Harbourmaster action from EOS main. EOS is the deliberate exception: Harbourmaster must be able to test changes to its own workflow and runtime before those changes merge.

For a same-repository, non-draft pull request in TilloTech/eos, harbourmaster-review.yml must:

  • check out the pull request head as the Harbourmaster runtime workspace;
  • check out the same head SHA again at .harbourmaster/repository;
  • invoke the local ./apps/harbourmaster action;
  • pass workspace_root: .harbourmaster/repository.

The action verifies that the reviewed checkout's Git HEAD equals the requested SHA before reviewer execution. Fork pull requests remain excluded by the job guard.

Consumer workflows check out the reviewed head once and invoke TilloTech/eos/apps/harbourmaster@main. They must not copy the EOS dual checkout or the local action path. The consumer checkout pin can differ from the EOS pin. EOS owns the pins inside the Action.

This exception intentionally allows an EOS pull request to change code that runs with the reviewer job's GitHub and AWS permissions. That tradeoff is limited to branches in the EOS repository, where push access is already trusted. It must not be generalized to consumer repositories or implemented with pull_request_target.

The following behavior is therefore a repository invariant:

  • EOS self-reviews execute the local action from the exact pull request head and review the checkout at .harbourmaster/repository.
  • Consumer reviews execute the Harbourmaster action from EOS main.
  • Both paths use the native pull request job's same-repository and non-draft guard, pass the event's exact head SHA, and verify the reviewed checkout against it.

For the consumer workflow, see How to add Harbourmaster reviews to a repository. The original auth and fork decision is in AI-50.