Security architecture
Workflows use least-privilege job permissions and immutable third-party action revisions.
Privileged pull_request_target jobs read trusted workflow code and may inspect untrusted
content but cannot execute it. AI tools edit only named scopes; trusted shell steps verify
paths and publish one commit. Secrets never enter prompts or artifacts. The merge train
rechecks exact-head gates immediately before mutation.
Issue text is an adversary-controlled input to a privileged job, so issue automation
narrows it twice. First by authority: an issue from outside the configured owner and
trusted-author set cannot start a solution job until a maintainer applies the configured
label, unless the repository explicitly sets solve_untrusted_authors. Second by shape: a
trusted step renders the issue into a bounded briefing file, the agent is told it is a
report rather than an instruction and reports attempted redirection through
prompt_injection_observed, and nothing derived from issue text reaches a shell command, a
workflow expression, or a branch name. Branch names come from the issue number and a hash
of its content, are validated against the configured namespace and permanent branches, and
are published by a trusted step the agent cannot reach.
Advanced branch diagnostics are opt-in and metadata-only. Events exclude application
values, exception text, arguments, locals, environment values, and secrets. Each JSONL
record carries invocation and GitHub correlation, a monotonic sequence, and the preceding
record's SHA-256 digest; recomputing the chain detects truncation, reordering, or mutation
within the retained stream. Operators must protect and expire VIBEY_GH_DEBUG_LOG like
other diagnostic telemetry. The hash chain is tamper-evident, not a digital signature;
ship it to append-only or independently authenticated storage when adversarial log writers
are in scope.
The managed CodeQL workflow analyzes Python changes on both delivery branches and their pull requests. The API-drift workflow independently verifies that every canonical capability remains available through MCP, API, CLI, SDK, and webhook boundaries.
Claude Code Action requires a Git repository at the workspace root for its own setup.
Managed workflows satisfy that contract with a disposable repository containing no source
checkout or persisted credential. It has a clean repository URL as origin because the
action requires that remote during setup. A deliberately nonmatching non-write-user sentinel
forces the action's credential-helper and secret-scrubbing path, so the token is never
embedded in .git/config and no additional actor is authorized. Untrusted source remains
under target/, checked out with persist-credentials: false. The disposable context is
removed with always() before trusted persistence or publishing. Only the later trusted
publish step attaches GH_TOKEN through gh auth setup-git; no Claude step can read that
authenticated context.
Claude observability is sanitized by default. The action's track_progress input is
strictly gated to its supported direct PR/issue events; privileged automation events rely
on job-phase visibility so an unsupported progress mode cannot fail the review itself.
Raw show_full_output logging can expose
assistant messages, tool results, repository contents, and CI material, so managed
workflows accept it only from an explicit manual dispatch when configuration opts in and
GitHub reports private repository visibility. Public and event-triggered runs fail closed.
Execution logs may instead be retained as access-controlled 90-day workflow artifacts.
Before a repair session, trusted automation downloads failed-check metadata and available failed-job logs for the exact PR head into a bounded local diagnostic bundle. The bundle is treated as untrusted input, capped at 200,000 bytes, and read-only to the diagnosis; repository code is still never executed in the privileged job. This avoids speculative repairs when optional CI MCP tools are unavailable without granting Claude shell access.
Repair and conflict-resolution publication never trusts the head it evaluated. The trusted
publisher re-reads the PR's current head immediately before committing and again after any
non-fast-forward push rejection. A mismatch means a human or another bot advanced the PR
concurrently, so the run is discarded as a stale no-op: it never force-pushes, never
overwrites the newer commit, never consumes a repair attempt, and never mutates a permanent
branch from an obsolete checkout. This applies even when develop or main is itself the
PR head during a promotion, which is exactly when clobbering unreviewed newer content would
be most damaging. See Workflows for the exact recheck points.
Repair and solve budgets persist in a single marker comment per PR or issue, located by
pattern match rather than by comment author (see Threat model). On a
public repository any commenter can forge or edit that marker to reset attempts or
heals, so the stored counters are a cost control, not an access control. They cannot
authorize an unreviewed merge: the review job re-runs on every SHA that reaches ready or
review, and the exact-head recheck above still gates every publish independently of the
stored state.
The Conventional Commits job is the sole guarded exception that may force-update history.
It can act only on a same-repository topic branch, only from an exact checked SHA, only on
linear history, and only with --force-with-lease. Permanent branches are rejected by
both configured and literal names. The job executes the trusted normalizer, never PR code,
and a concurrent contributor push makes the lease fail closed.
Promotion PRs from the integration branch to the release branch skip that history
normalizer entirely. Provenance still checks the complete repository state, but does not
re-audit or rewrite historical subjects already admitted to the protected integration
branch.
The automation-bootstrap workflow is a second guarded exception: a manually dispatched,
admin-only squash merge that bypasses the ordinary PR-automation review because privileged
workflow code is loaded from the trusted base branch and a PR cannot self-repair it. It
requires administrator permission on the actor, an open non-draft PR that exactly matches
the dispatched head SHA and targets develop, changed files confined to workflow,
template, or automation-core paths, and every non-gate check run on that exact SHA —
including CodeQL, API drift, documentation, provenance, build, and lint — completed
successfully before the --match-head-commit merge runs. It never deletes a permanent
branch. See Threat model for the full rationale.
Webhook receivers must use a strong VIBEY_GH_WEBHOOK_SECRET, verify HMAC over the exact
raw body, and place VIBEY_GH_WEBHOOK_STATE_DIR on access-controlled durable storage.
Accepted IDs use atomic mode-0600 marker creation, preventing replay across restarts and
concurrent CLI processes. Operators own TLS, rate limits, request-size limits, backups,
retention, and safe pruning of expired claims.