feat(link): establish + regenerate + lock journal id at onboarding master
Move the journal's machine instance_id from lazy random-UUID creation to a CA-derived, self-certifying id established during onboarding. Owner can regenerate a candidate CA mark until happy, then lock it in permanently; the locked id is str(jid_from_spki(<permanent CA SPKI>)) — wiring up mark.py for the first time. - ca.py: write key-then-cert (cert is the committed marker, last byte); add ca_is_present() and promote_ca() (staged -> permanent, key-then-cert). - link/establish.py (new owner module): staged candidate CA lifecycle, regenerate, crash-safe lock-in, and create_link_state() — the self-healing CA-derived replacement for the lazy random-UUID mint. All staging/permanent/ state mutations serialize under hold_lock(link/identity); committed-ness is the on-disk CA, never a config flag or locked_at. - paths.LinkState: additive locked_at field + jid accessor; load_or_create derives from the CA instead of uuid.uuid4(). Existing persisted ids are kept verbatim (forward-only); legacy lazily-created journals read as committed. - convey: GET /init/mark, POST /init/mark/{regenerate,lock} routes + an init_finalize gate that refuses completion until the id is locked (identity_not_locked). Routes return Mark.to_render_spec() + a locked bool. - Lockstep: establish.py added to journal-io OWNER_FILES + the AGENTS.md L2 Link row (link/ca-staging/** + state.json locked_at); regenerated the convey-clients OpenAPI contract for the new global reason-enum entry. The id stays a string instance_id everywhere external (relay ?instance=, validators, attestation claims, pair-link UUID bytes, portal schema); the new value is a conformant UUIDv8 that round-trips through version-agnostic decoders. The id is a routing/display identifier only — auth stays bound to the CA cert (mTLS) and authorized_clients fingerprints, never the id alone. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>