These are the product’s technical docs at this revision. Historical version names and status notes are preserved; see Releases for download qualifications.
Read as MarkdownSaved migrations and offline continuation
On this page
internal/migration defines the provider-neutral plan, intent, receipt and
Destination contract. Root migration_*.go owns native admission and the local
filesystem. Provider registration and the guided/scripted CLI are separate
consumers of these boundaries. Neither planning nor importing executes workflows,
fetches dependencies, selects audit authority or reads ambient credentials.
A caller obtains a snapshot through LocalMigrationSource (explicit selected Git
refs), ArchiveMigrationSource, or an optional provider migration.Source.
The returned native/archive blob source owns a temporary spool and must be closed
by its caller. PrepareMigration takes the observed snapshot, blob source,
selection, explicit destination and decisions, and writes a new private session
directory. The parent directory must already exist. Existing session directories
are refused. For repository operations put sessions below the resolved Git
directory, for example .git/hn/migrations/<operator-selected-name>; keep them
outside source/worktree and archive selections. OpenMigrationSession opens the
same saved observation; ApplyMigration applies or resumes it. No source refresh
occurs during resume. InspectMigration reads attributed records, links and
verified blob bytes without an adapter or private signing identity.
The canonical hn.migration-plan/1 binds snapshot digest, destination, limits,
explicit findings/decisions, dependency-ordered intents and expected destination
baselines. Plans have no credential fields and do not capture environment values,
private signer stores, audit anchors or working files. Exact selected committed
content remains exact, including any secrets someone previously committed.
Decisions identify a specific finding and use acknowledge, map or preserve;
all unsupported, missing-dependency, identity-conflict and requires-mapping findings
need an explicit decision. A preserved finding describes retrieved bytes, never
an inaccessible URL or unobserved record. State/type mappings remain destination
planning decisions and may not be silently rewritten during application.
Local and archive destinations
LocalMigrationDestination requires an existing explicit Git repository (which
may be a fresh bare repository). It binds the physical Git directory, object
format, HEAD selection and expected-old values of every destination observation.
The local/governance finding requires an explicit decision to preserve historical
source governance. This operation installs observations and an offline archive;
it does not activate source governance. Existing working files, branches,
policies, signing streams and private audit authority remain controlled locally.
Activating governance or adopting an audit authority is a later explicit operator
operation through the ordinary native commands.
Imported actor/proposal/memory streams use deterministic refs/hn/remotes/
observations scoped to the immutable source ID. Code observations use the same
scope under code/<digest-of-original-ref>; the saved snapshot retains each
original name. Code observations are never checked out or substituted for HEAD.
All admitted objects are copied by explicit object ID before an expected-old Git
ref transaction. Imported actors do not become local signing identities. A local
signer can create new native events referencing the observed histories afterward.
Retrieved foreign records, source-only conversations, links and downloaded blobs
remain in .git/hn/imports/<snapshot-digest>.tar. ReadLocalMigration validates
and opens that immutable archive for offline inspection and re-export. Foreign
reviews, users and workflows remain descriptive/inert; they grant no native
permissions. This code imports no provider clients and also works when adapters
are excluded. Existing nogithub behavior is unchanged.
ArchiveMigrationDestination takes an explicit absolute new output path. It
writes verified canonical archive bytes to a private temporary regular file and
publishes without replacement. Archive import/export uses the same snapshot and
native quarantine validation as local migration.
Native admission and audit integration
Maintain the canonical project snapshot bridge for read-only export and bounded audit auxiliary admission after approved WP02/WP04 integration. Export first copies the bounded selected ordinary Git closure into disposable inspection staging. Importing native readers only run there. Source refs, worktree and object database remain unchanged. Signed legacy code dependencies are then read from the source, and final raw material passes ordinary full-history checking.
Stage checks raw hashes and ordinary full Git structure before a native reader
unpacks an audit attachment. The canonical audit pack measurement accounts for
the union of ordinary and expanded audit object IDs against saved Git object,
per-object and aggregate limits before unpacking; shared IDs count once and
ambient destination objects grant no discount. Canonical native signature, pack
binding/shape, actor-chain, role and relationship validators admit facts. Audit
packs intentionally retain snapshot/path-proof closure: missing commit parents
or unrelated proof siblings are allowed there, while incomplete ordinary history
is refused. ProjectQuarantine.Roots() contains ordinary traversal roots only;
ObjectIDs() contains the exact admitted ordinary-plus-audit union. No duplicate
audit admission implementation is introduced.
Defaults are 10,000 records, 1,024 attachments/refs, 16 MiB per attachment or
manifest, 256 MiB aggregate attachments, 100,000 Git objects, 16 MiB per Git object
and 512 MiB aggregate expanded Git bytes, plus separately bounded raw framing.
Every saved declared blob is bounded, digest-verified and spooled before effects.
ApplyContext.OpenBlob opens only a declared verified spool entry, not a path or
URL. Later tampering is refused before writes.
Recovery and dependent writes
Private HMAC-authenticated plan/snapshot/progress files and a separate random
private authentication key live in the session directory. These detect altered
checkpoints; possession of the directory/key is local operator authority, not
remote or native signing authority. Regular-file/permission/path checks reject
symlinks. An exclusive lock covers the whole apply/resume operation. A crashed
process may leave apply.lock; inspect the owning process before manually
removing that stale lock. Resume still reconciles every in-flight write.
The shared filesystem policy distinguishes macOS's
verified root-owned /var and /tmp aliases from untrusted user links. Session
create/reopen and internal spools use the same normalization, including in child
environments without TMPDIR; every remaining symlink and unsafe private leaf
continues to refuse.
Each immutable intent moves through prepared, inflight, confirmed. Before
in-flight state, the engine resolves typed references only from confirmed
predecessor receipts. It persists canonical actual payload bytes and digest;
apply and reconciliation receive copies of that same BoundWrite. For example,
a comment referring to the issue-create receipt uses exactly its confirmed
remote_id, including after a crash. Optional providers own no hidden parent-ID
map. RemoteRef has exactly one present branch (id or receipt); create
target must be absent, update target required. Unknown/duplicate/null fields,
case aliases, invalid Unicode and missing required source fields are refused by
the shared strict JSON check and the sole closed write validator.
A receipt must match plan, intent, kind/action, destination, actual payload digest
and observed identity/revision. An uncertain write stays in flight. Only exact
reconciliation with a matching receipt confirms it. ambiguous and no_proof
require manual recovery; negative lookup, timeout or inaccessible content never
authorizes repeating a create. The engine performs no automatic mutation retry.
Git objects, imported archive files, ref transactions and independent provider writes cannot share a global transaction. Local application preflights stale refs before publication, saves the verified archive, copies objects and atomically promotes refs. A failure between these steps can leave a partial archive/object installation. Reconciliation confirms only when the exact verified archive and all intended refs are present; otherwise it reports manual recovery. Archive reconciliation similarly verifies the exact saved snapshot. Successful resume needs the saved session and destination, not the original source/provider.
Shared provider and Git composition
ComposeMigrationSources(primary, additional...) joins a provider communication
source with LocalMigrationSource{GitDir: ...} using Selection.Refs for explicit
already-present ordinary code history. The result is one canonical snapshot and
Close-able blob source, preserving the primary provider's immutable identity and
original record attribution. Only one child supplies native Git material. This
boundary performs no provider-specific cloning or implicit network fetch; a
caller selects its available local Git repository explicitly.
ComposeMigrationDestinations(communications, code) routes communication intents
to the selected adapter and git_refs intents to the shared
GitMigrationDestination{Remote, Destination, Mappings}. Mappings explicitly pair
source refs and destination branches/tags. The closed
hn.migration.git-write/1 payload binds snapshot digest, explicit remote and
ref/oid/expected_old values. No provider invents a code copier or embeds
arbitrary executable URLs. Supported transports are explicit absolute local
repository paths and credential-free HTTPS endpoints; remote helpers, relative
paths, embedded credentials, SSH command forms and other protocols are refused.
No Git credentials are discovered or saved by this seam; explicit transient authentication is described below.
Preparation proves every changed destination branch is an ancestor of its selected source head. Existing tags cannot be replaced. Before any provider side effect, apply rechecks pending Git baselines. A single atomic Git push uses exact leases as compare-and-swap guards after the independent fast-forward proof; divergent history is refused. Push does not delete source/destination refs, force divergent history, check out code or activate foreign workflows. The remote must support atomic push. There is no transaction spanning provider writes and Git; committed progress exposes any partial result. A lost Git acknowledgement stays in flight; reopening the same saved plan confirms only an exact positive advertisement of all intended remote ref IDs. Missing or different refs require manual recovery.
Git code capture requires a complete already-present ordinary local repository;
partial/promisor sources are refused before object queries to avoid lazy fetching
or source object-database mutation. Intentional partial audit proof closure is a
separate signed native format and remains supported. The shared Git remote client
uses isolated configuration with local/public HTTPS defaults. Private HTTPS
requires GitMigrationDestination.Credentials containing explicit
GitHTTPSCredentials{Origin, Authorization, CAFile}. Authorization is bounded
Basic or Bearer input bound to the exact selected HTTPS origin. These fields are
excluded from JSON and remain only in the Git child's environment/configuration,
never a saved plan/progress/receipt, URL, command argument or diagnostic. Resume
requires fresh credential input; provider API tokens are never reused implicitly.
Credential helpers and URL rewrites are absent from the isolated configuration;
redirects are disabled, including across origins. TLS verification always remains
enabled; CAFile optionally supplies an explicit trust file, including for a local
TLS fixture. Git/server failures return generic redacted errors with actionable
access/authentication guidance. Existing
operator-controlled destination Git reference-transaction hooks retain normal Git
behavior. A hook refusal is an uncertain guarded write requiring reconciliation;
source hooks and imported pipelines are never executed by capture/import.
Explicit continuation from imported observations
LocalMigrationSource captures selected current Git refs only. To retain imported
foreign communications during a later local-to-archive or local-to-provider
migration, discover bounded saved IDs with ListLocalMigrations(gitDir, limits),
then explicitly select ImportedMigrationSource{GitDir, SnapshotDigests} and join
it with the current Git source through ComposeMigrationSources. The imported
source reuses ReadLocalMigration validation and contributes attributed foreign
records and non-Git blobs only. It never reselects stale archived code or private
journals. Current code/native histories still require explicit current ref
selection. Identical record/blob observations deduplicate; conflicting revisions
or metadata require an explicit snapshot choice. Aggregate record/blob budgets
apply across selected imports. Missing selected imports refuse the observation;
unselected imports are explicitly outside observed coverage.
Every destination implements required Locator() migration.Locator from its
resolved configuration. Planning checks equality with the selected destination;
apply/resume checks equality with the saved destination before preflight and
before each intended write. Composed destinations expose a valid locator only
when every child agrees. A changed provider configuration cannot create an
object first and discover the mismatch only in its receipt. Adapters must also
check immutable remote project/parent identity and their expected revision guards;
matching locator text alone does not prove a remote repository was not replaced.
Preparation durably seals an authenticated empty progress checkpoint alongside the plan and observation. Its first apply binds the actor before effects. Missing progress is damaged recovery evidence, even if the plan/snapshot still verify; open/apply refuse it instead of creating a new journal and repeating writes.
Git HTTP children receive a disposable private empty home/config directory and
consistent platform aliases through their own process environment. Parent
home/configuration and files are never changed. Ambient NETRC/CURL_HOME overrides
are removed from that child so libcurl cannot consult an unrelated .netrc or
_netrc; helpers/rewrites remain disabled. A real synthetic outer credential
store regression proves unauthenticated private access does not consult/send
that marker, explicit supplied authorization still works, and outer files and
parent environment remain unchanged. Certificate verification remains enabled.