Login-minted gateway credential on the client (LLP 0061)#228
Conversation
hyp remote login now captures the gateway_jwt/gateway_expires_at/gateway_id fields a login-configured server returns on the authorization_code grant, sends an advisory host label (default os.hostname(), --host to override), and seeds the credential into every @hypaware/central sink whose url shares the target's origin, at the sink's persisted identity path. The sink's acquire() loads a login seed with no bootstrap token; an origin: 'login' marker keeps the re-enrollment guard from reading the missing token fingerprint as a mint mismatch, while the central_url re-point guards apply unchanged. Covered by unit tests and the extended remote_oidc_login smoke.
Review: Login-minted gateway credential on the client (LLP 0061)Overall this is a clean, well-documented change: the login/seed/consume split matches LLP 0061, the persisted-path and origin-matching derivations line up with what the central sink actually uses, and the guard behavior is well covered by tests. Blocking: CI failure is real, caused by this PRThe red CI checks are a genuine defect, not flakiness. All three jobs (
import { writeLoginSeed } from '../../../hypaware-core/plugins-workspace/central/src/identity_client.js'The declaration build compiles only Note the repo convention (CLAUDE.md) that Non-blocking: seeding can target an uninstalled central sink
Everything else (error reporting on partial |
The published declaration build (`npm run build:types`, `rootDir: src`) compiled `src/core/remote/gateway_seed.js`, which imported `writeLoginSeed` as a value from `hypaware-core/.../central/src/identity_client.js`. A value import pulls that out-of-root file into the program, so tsc failed with TS6059 and CI's `npm i` -> `prepare` -> `build:types` went red (local `npm test` / typecheck / lint never run build:types, so it looked clean). Move the seed writer to `src/core/remote/gateway_seed.js`, the login/seed producer side of the package boundary. The sink (`hypaware-core`) only reads and refreshes the persisted identity; the file format is the contract between the two halves and the sink validates it on read. `src` no longer imports a sink value; the only cross-boundary reference left is the type-only `PersistedIdentity` import, which is erased. LLP 0061 D5 and the @ref move with the writer.
Client half of login-minted gateway enrollment (LLP 0061; server half already shipped). A single
hyp remote loginnow yields both credentials: the query session, and the gateway credential seeded where thecentralforward sink reads it, so a logged-in user forwards logs with no bootstrap-token distribution.Changes
exchangeCode()readsgateway_jwt/gateway_expires_at/gateway_idintoOidcSession.gatewayand sends an advisoryhostlabel (defaultos.hostname(),--host <label>to override). A server that omits the fields yields no gateway; a partial set fails loudly. The gateway fields never land in the query-scopedoidcrecord (D1).@hypaware/centralsink whoseurlshares the target's origin, atidentity.persisted_pathor the per-plugin default (D5). The seed is the sink's persistedidentity.jsonpre-populated, stamped withcentral_urlandorigin: 'login'(D2/D4). A displaced identity is reported, never silent.acquire()loads a login seed with no bootstrap token; theoriginmarker exempts it from the token-fingerprint mint check while thecentral_urlre-point guards apply unchanged, and a re-point refusal now advises re-login instead ofhyp join(D3/D4). Refresh preserves the provenance.Testing
exchangeCode, seed writer + origin-aware guards in the central identity client, and command-level seeding through the real config resolution (default and custom paths, origin matching, replacement notes, failure reporting).remote_oidc_loginsmoke extended: the stub server mints a gateway on login; the flow seeds it and proves the sink'sIdentityClientloads it without a bootstrap token.npm test(1796 pass),npm run typecheck,npm run lintall clean.