Skip to content

F1: Read-only Git document access

F1 adds the smallest repository-read surface needed by a continuity client. It does not copy a repository into HeyAira and it does not create a GitHub App, push code, open pull requests, or change deployment infrastructure.

Contract

The authenticated project is the tenant boundary. A local administrator first links a GitHub repository and allowlists a document at one immutable commit:

heyaira-admin add-document-source \
  --project-id <project-id> \
  --github-owner h8v6 \
  --github-repository heyaira \
  --document-role runtime \
  --path docs/CONTINUITY_PROTOCOL.md \
  --commit-sha <40-character-commit-sha>

The command stores only repository identity, role, path, commit SHA, and a fragment limit. It is an administrator operation; there is no model-facing write operation for changing the allowlist.

For workflow instructions, --track <branch> replaces --commit-sha and defers commit resolution until dispatch. See document-source operations. Standalone F1 reads continue to require pinned sources.

Catalog dispatch also reads account-scoped instruction sources, configured only through the admin CLI. These are restricted to docs/agents/ and the instruction role. The core and worker/verifier mode precede role text, independently of the task project's repositories. Account tracking sources always resolve the named branch head at dispatch, with provenance tracking:<branch>; the frozen workflow keeps that commit for replay and corrections. They grant no standalone document access or access to another project's data. See account source operations.

Each catalog profile exposes layers metadata in assembly order: core, mode, role responsibilities, methodology, optional role instructions, soul, and skills. Hashes and character counts describe each layer's raw text (without the assembly headings); est_tokens=ceil(chars/4) is an estimate. Core and mode count towards the existing 60 KiB source-text budget per profile. Missing account configuration preserves the previous instructions and reports catalog.layers_missing (core and/or mode). Configured sources that cannot be read fail dispatch. Source and repository revocation or configuration changes are checked again before the contract is stored. Compact workflow views retain layer metadata.

runtime_bootstrap_get() returns the authenticated project's active allowlisted references (tracking-only sources have a NULL commit and a tracking_ref branch name and need workflow dispatch; pinned reference fields are unchanged). The response contains source_id, repository identity, role, path, commit SHA, and limits. If no active runtime source exists, it returns runtime_unconfigured.

git_document_get(source_id, start_byte?, max_bytes?, cursor?) reads one allowlisted source. The server resolves the source from the authenticated project and calls the fixed GitHub Contents API host with the pinned commit SHA. The model cannot supply an arbitrary repository URL, branch, or path.

The response contains the UTF-8 content fragment, byte range, full-file SHA-256, fragment SHA-256, and an opaque next_cursor when more content is available. A cursor is signed and bound to the project, principal, source, commit, path, offset, and expiry. Each continuation re-checks authorization and re-reads GitHub; F1 has no cache.

Limits and failure behavior

  • documents:read is a separate OAuth scope; existing OAuth grants are not silently expanded.
  • Legacy static project tokens retain only their historical memory/task permissions. They do not receive documents:read; a document read needs an explicit OAuth grant containing that scope.
  • Only ordinary UTF-8 files are accepted. Directories, symlinks, gitlinks, invalid UTF-8, path traversal, and non-UTF-8 byte ranges are rejected.
  • A file is limited to 1 MB and a response fragment to 64 KB.
  • A full 40-character commit SHA is required for standalone F1 reads. Workflow tracking branches resolve separately at dispatch; tag selectors are not accepted.
  • The GitHub Contents JSON response is bounded to 1,500,000 bytes before JSON parsing. This leaves room for base64 and JSON overhead around a valid 1 MB decoded file while bounding responses without or with a false Content-Length. Redirects are rejected rather than followed.
  • Provider failures are returned as stable codes such as provider_not_found, provider_forbidden, provider_unavailable, or provider_unconfigured; credentials and response bodies are never logged.

The provider follows GitHub's repository contents read contract and uses a read-only injected credential. GitHub documents the path and ref inputs and the Contents read permission in its repository contents API documentation. Production uses a GitHub App installation token. The server reads the App ID from HEYAIRA_GITHUB_APP_ID and a private key from the protected file named by HEYAIRA_GITHUB_APP_PRIVATE_KEY_FILE, mints short-lived installation tokens, and caches them until shortly before expiry. The repository binding's github_installation_id selects the installation; the HeyAira MCP credential is never reused as a GitHub credential. If either the App key or installation binding is absent, the tool returns provider_unconfigured.

What F1 does not claim

F1 does not provide a HeyAira state snapshot, event sourcing, background routing, thread wake-up, GitHub App installation, or public GitHub access. The tests use an injected synthetic provider and therefore are not evidence of a public GitHub deployment. Production credential wiring and external verification remain a later, explicitly authorized stage.

Local verification

.venv/bin/pytest

The document tests exercise allowlist-shaped source records, UTF-8-safe fragments, signed cursor binding, object/encoding/limit failures, and the fixed-host GitHub adapter. They do not contact GitHub and do not require a production token.

The full MCP-to-document path is covered by an opt-in integration test. Point HEYAIRA_F1_E2E_DATABASE_URL at a newly created isolated PostgreSQL database before running it; the test starts a local MCP server, inserts synthetic project/OAuth/allowlist state, uses a synthetic Git provider, verifies bootstrap and continuation, revokes the source between reads, and confirms a legacy token is denied before the provider is called:

HEYAIRA_F1_E2E_DATABASE_URL=postgresql://... \
  .venv/bin/pytest -q tests/test_f1_mcp_e2e.py