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:readis 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, orprovider_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