LearnNewsExamplesServices
Frontmatter
id15760
titlePreflight tenant-repo access before scheduled ingestion
stateClosed
labels
bugaitestingarchitecturesecurity
assigneesneo-gpt
createdAtJul 23, 2026, 5:07 PM
updatedAtJul 23, 2026, 11:26 PM
githubUrlhttps://github.com/neomjs/neo/issues/15760
authorneo-gpt
commentsCount1
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJul 23, 2026, 11:26 PM

Preflight tenant-repo access before scheduled ingestion

Closed Backlog/active-chunk-9 bugaitestingarchitecturesecurity
neo-gpt
neo-gpt commented on Jul 23, 2026, 5:07 PM

Context

Server-side tenant-repo ingestion has a secret-safe configuration contract, a GitMirror acquisition primitive, periodic scheduling, and post-attempt diagnostics. What it lacks is a pre-attempt answer to the deployment question that matters: can this orchestrator resolve the configured credential reference and read the configured repository/ref?

Live latest-open sweep: checked the latest 20 open issues at 2026-07-23T15:06:53Z; no equivalent found. The recent all-state A2A claim sweep found no overlapping lane. Semantic and exact-history sweeps surfaced closed predecessors #11731, #11787, #11788, #11790, #13432, #14402, and #15749; their shipped contracts stop at syntactic config validation, credential injection during Git work, AiConfig-leaf requiredness, or post-attempt diagnostics.

The Problem

The current access contract validates that credentialRef is nonempty, but it does not prove the reference type is supported, the referenced secret/key exists in the orchestrator process, or the credential can read the configured repository/ref.

There is also a concrete grammar mismatch: the tenant-repo contract accepts values such as helper:github-app-installation, while gitMirror.mjs interprets every unrecognized string prefix as an environment-variable name. A syntactically accepted value can therefore be guaranteed to fail only later.

Credential resolution happens inside GitMirror when clone/fetch runs. TenantRepoSyncService applies cadence/jitter before that operation, so a new or changed deployment may report its config as available and remain infrastructure-healthy until a repository becomes due. DeploymentStateBridgeService can expose the failure after an attempt, but it cannot currently certify access readiness before one.

This turns deterministic deployment defects into delayed ingestion failures and leaves “healthy service, zero documents” as a plausible steady state during the delay.

The Architectural Reality

  • tenantRepoAccessContract.mjs owns the durable, no-secret configuration grammar.
  • gitMirror.mjs owns credential resolution, Git process construction, timeout handling, and secret redaction. A readiness probe must reuse that exact boundary rather than implement a second credential path.
  • Repository-host token type/scope inference is not portable. The provider-neutral contract is a capability proof: the configured credential can read the configured clean URL and ref.
  • Multi-repo deployments require failure isolation. One inaccessible repo degrades ingestion readiness for that repo without stopping unrelated configured repos from syncing.
  • The public deployment snapshot must remain bounded and secret-free. It may expose hashed repo identity and stable categorical results, never URLs, credential refs, env names, key paths, usernames, raw stderr, or token metadata.
  • This is tenant bootstrap/access configuration, not a new AiConfig leaf. If an implementation nevertheless touches AiConfig, ADR 0019 applies: read resolved leaves at use sites and add no parallel env-resolution path.

Structure-map ownership: existing ai/services/knowledge-base/helpers/ access/GitMirror boundary, existing orchestrator tenant-sync service and deployment-state bridge, plus existing cloud-deployment guides. No new daemon or public write tool is required.

The Fix

  1. Canonicalize the supported credentialRef grammar. Reject unknown/unsupported schemes during effective tenant-repo config resolution; never reinterpret an unknown scheme as an env-var name.
  2. Add a secret-free local-resolution check for supported env, file, and SSH references using the same resolver that clone/fetch uses.
  3. Add a bounded, read-only repository capability probe through GitMirror (for example git ls-remote --exit-code with the existing askpass/SSH/redaction/timeout path) against the clean URL and configured ref.
  4. Run the preflight for every enabled effective repo at bootstrap and when the effective config/credential fingerprint changes. Normal clone/fetch remains authoritative after credential rotation; deployment inspection reads cached bounded outcomes instead of launching network work per read.
  5. Project per-repo hashed readiness plus aggregate counts into tenant-repo deployment diagnostics. Keep process liveness healthy, but mark ingestion readiness degraded or unknown when capability is not proven; any deployment-certifying preflight fails closed.
  6. Document the distinction between local credential-ref resolution, remote capability proof, and a later scheduled ingestion result.

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback / Edge Case Docs Evidence
credentialRef grammar tenantRepoAccessContract.mjs + gitMirror.mjs One explicit supported scheme set; unknown schemes reject at config resolution none remains valid only where unauthenticated access is intentionally supported Tenant config contract Grammar matrix tests
Local credential readiness Existing GitMirror secret resolver Return bounded ready/missing/invalid/uncheckable state without secret material Never return the ref name/path/value Troubleshooting map env/file/SSH fixtures + deep redaction scan
Remote repository capability Existing GitMirror process/redaction boundary Bounded read-only probe proves repository/ref readability Timeout/transport/denied/not-found/ref-missing remain distinct stable categories where Git permits Pull-ingestion preflight fake Git/remote fixtures and timeout tests
Deployment snapshot DeploymentStateBridgeService Aggregate readiness plus hashed per-repo status/timestamp/code No attempt yet or expired proof reports unknown, never ready Deployment inspection schema mixed-repo snapshot tests
Scheduler TenantRepoSyncService Unaffected repos continue; failed preflight is visible before cadence-delayed clone/fetch Normal fetch remains authoritative after rotations/races Operator runbook isolation and config-change invalidation tests

Decision Record impact

Aligned with ADR 0014's cloud scheduler topology and ADR 0019's config authority. No ADR amendment: this closes a readiness gap at existing tenant-config, GitMirror, scheduler, and diagnostic boundaries.

Acceptance Criteria

  • Supported credentialRef forms are explicit and shared by config validation and GitMirror resolution.
  • An unknown scheme such as helper:* is rejected as unsupported rather than treated as an env-var name.
  • Missing/empty env secrets, missing/unreadable file secrets, and missing/unreadable SSH keys are detected before a scheduled clone/fetch and reported only through stable redacted codes.
  • A bounded read-only Git probe distinguishes ready, timeout/transport failure, denied-or-not-found, and ref-not-found outcomes without provider-specific token inference.
  • Preflight runs at bootstrap and when effective config/credential fingerprints change; deployment inspection consumes stored results and does not perform unbounded network work.
  • Process liveness remains healthy while ingestion readiness is degraded/unknown; a certifying deployment preflight never reports ready when any enabled required repo is unresolved or unreachable.
  • One failed repo does not block unrelated repos from syncing.
  • Public diagnostics contain only aggregate counts, hashed repo identity, status, timestamp, and allowlisted codes; deep tests prove absence of clone URLs, refs, env names, file/key paths, usernames, tokens, raw stderr, stacks, and host paths.
  • Tests cover supported grammar, unsupported helper:*, env/file/SSH resolution, remote ready/denied/timeout/ref-missing, config-change invalidation, and mixed-repo isolation.

Out of Scope

  • Inferring whether a secret is a PAT, group token, deploy token, or provider-specific scope set.
  • Writing/rotating credentials or tenant config remotely.
  • Exposing repository identities or secret-reference metadata in public diagnostics.
  • Replacing normal clone/fetch as the final authority after a readiness probe.
  • Adding shell, Docker exec, or a remote ingestion actuator.

Avoided Traps

  • Do not build a second credential resolver for diagnostics.
  • Do not probe on every inspect_deployment read; cache and invalidate bounded readiness evidence.
  • Do not make one tenant repository failure crash the whole orchestrator.
  • Do not collapse access states into raw Git message prose.
  • Do not claim token scope semantics from a provider-neutral Git capability check.

Related

  • #11731, #11787, #11788, #11790 — original tenant-repo ingestion contracts.
  • #13432 / Discussion #13505 — AiConfig leaf-owned requiredness; dynamic tenant credential references were outside that contract.
  • #14402 — deployment diagnostics predecessor; live remote credential validation was out of scope.
  • #15749 / PR #15754 — bootstrap provenance and post-attempt diagnostics.

Origin Session ID: fc1a49c1-e30a-4e3a-960a-e0596367a4c1

Handoff Retrieval Hint: tenant repo credentialRef preflight supported schemes GitMirror ls-remote ingestion readiness