@amalgm/shell 0.1.113 → 0.1.115

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/PURPOSE.md +67 -6
  2. package/README.md +38 -7
  3. package/dist/authorization/device-key.d.ts +19 -0
  4. package/dist/authorization/device-key.js +81 -0
  5. package/dist/authorization/device-key.js.map +1 -0
  6. package/dist/authorization/index.d.ts +65 -0
  7. package/dist/authorization/index.js +63 -0
  8. package/dist/authorization/index.js.map +1 -0
  9. package/dist/authorization/issuer.d.ts +11 -0
  10. package/dist/authorization/issuer.js +76 -0
  11. package/dist/authorization/issuer.js.map +1 -0
  12. package/dist/authorization/key-store.d.ts +15 -0
  13. package/dist/authorization/key-store.js +148 -0
  14. package/dist/authorization/key-store.js.map +1 -0
  15. package/dist/authorization/recovery-client.d.ts +19 -0
  16. package/dist/authorization/recovery-client.js +102 -0
  17. package/dist/authorization/recovery-client.js.map +1 -0
  18. package/dist/authorization/recovery-host.d.ts +28 -0
  19. package/dist/authorization/recovery-host.js +93 -0
  20. package/dist/authorization/recovery-host.js.map +1 -0
  21. package/dist/authorization/recovery.d.ts +13 -0
  22. package/dist/authorization/recovery.js +27 -0
  23. package/dist/authorization/recovery.js.map +1 -0
  24. package/dist/authorization/secret-process.d.ts +2 -0
  25. package/dist/authorization/secret-process.js +32 -0
  26. package/dist/authorization/secret-process.js.map +1 -0
  27. package/dist/boot.d.ts +19 -11
  28. package/dist/boot.js +34 -25
  29. package/dist/boot.js.map +1 -1
  30. package/dist/builtin-agents.d.ts +3 -0
  31. package/dist/builtin-agents.js +15 -0
  32. package/dist/builtin-agents.js.map +1 -0
  33. package/dist/chat-composition.d.ts +19 -0
  34. package/dist/chat-composition.js +83 -0
  35. package/dist/chat-composition.js.map +1 -0
  36. package/dist/chat-host.d.ts +9 -25
  37. package/dist/chat-host.js +12 -158
  38. package/dist/chat-host.js.map +1 -1
  39. package/dist/cli.js +21 -13
  40. package/dist/cli.js.map +1 -1
  41. package/dist/content-cache-host.d.ts +7 -0
  42. package/dist/content-cache-host.js +23 -1
  43. package/dist/content-cache-host.js.map +1 -1
  44. package/dist/credentials-host.d.ts +1 -5
  45. package/dist/credentials-host.js +1 -5
  46. package/dist/credentials-host.js.map +1 -1
  47. package/dist/entity-record-store.d.ts +2 -0
  48. package/dist/entity-record-store.js +15 -0
  49. package/dist/entity-record-store.js.map +1 -1
  50. package/dist/forward.js +1 -1
  51. package/dist/forward.js.map +1 -1
  52. package/dist/index.d.ts +7 -6
  53. package/dist/index.js +2 -2
  54. package/dist/index.js.map +1 -1
  55. package/dist/machine-host.d.ts +29 -0
  56. package/dist/machine-host.js +240 -10
  57. package/dist/machine-host.js.map +1 -1
  58. package/dist/parity.d.ts +1 -5
  59. package/dist/parity.js +4 -41
  60. package/dist/parity.js.map +1 -1
  61. package/dist/release-catalog.js +7 -4
  62. package/dist/release-catalog.js.map +1 -1
  63. package/dist/runtime-command-host.d.ts +11 -0
  64. package/dist/runtime-command-host.js +65 -0
  65. package/dist/runtime-command-host.js.map +1 -1
  66. package/dist/user-ground-host.js +60 -5
  67. package/dist/user-ground-host.js.map +1 -1
  68. package/npm-shrinkwrap.json +2326 -0
  69. package/package.json +15 -5
  70. package/dist/chat-payloads-host.d.ts +0 -8
  71. package/dist/chat-payloads-host.js +0 -76
  72. package/dist/chat-payloads-host.js.map +0 -1
  73. package/dist/chat-relay-host.d.ts +0 -24
  74. package/dist/chat-relay-host.js +0 -90
  75. package/dist/chat-relay-host.js.map +0 -1
  76. package/dist/drivers/claude-code.d.ts +0 -27
  77. package/dist/drivers/claude-code.js +0 -72
  78. package/dist/drivers/claude-code.js.map +0 -1
package/PURPOSE.md CHANGED
@@ -13,7 +13,9 @@ shell only assembles.
13
13
 
14
14
  2. **Bricks arrive through the registry.** Every `@amalgm/*` dependency is a
15
15
  published npm version — never a `file:` link, never a copied source tree.
16
- Upgrading the runtime means bumping a version, not merging code.
16
+ Upgrading the runtime means bumping a version, not merging code. Registry
17
+ proof runs under the supported Node executable that launched the gate;
18
+ ambient package-manager shims are not runtime evidence.
17
19
 
18
20
  3. **Parity gates each released service.** The target begins with
19
21
  `amalgm-engine`'s runtime surface, then subtracts only owner-approved
@@ -45,8 +47,8 @@ shell only assembles.
45
47
 
46
48
  8. **The shell is the production runtime for its signed release.** The `amalgm`
47
49
  bootstrap may install, supervise, and update the shell, but it never starts
48
- an Engine implementation of Core or Files beside it or falls back to copied
49
- Engine behavior. The current release is deliberately Core + Files only.
50
+ an Engine implementation beside it or falls back to copied Engine behavior.
51
+ The current release is Core + Files + Agents + Tools + Chat.
50
52
 
51
53
  9. **Login is Core followed by Live.** Core owns authentication, credential
52
54
  separation, installation verification, restart proof, and readiness. Its
@@ -209,6 +211,13 @@ shell only assembles.
209
211
  declared cargo is bounded, Shell supplies the cached manifest and blocks to
210
212
  Live for the same submit frame; otherwise it uses the ordinary missing-block
211
213
  effect. The cloud, not Shell, owns the final seal-before-number decision.
214
+ Cache bytes remain only while an unresolved durable operation names their
215
+ content head or while the current verified text or repository head is the
216
+ one exact base for that entity's next delta. Once Send acceptance is
217
+ durable, Shell removes every sealed artifact and manifest that neither a
218
+ pending Record nor a current replay base retains; restart performs the same
219
+ derivation from SQLite, so accepted history cannot accumulate as machine
220
+ storage.
212
221
 
213
222
  25. **Each cold stage performs only its necessary byte pass.** Registration
214
223
  reads every ordinary text or binary file once through the bounded capture
@@ -695,10 +704,62 @@ shell only assembles.
695
704
  across restart, and all pre-page journal shapes convert atomically without
696
705
  dropping an unknown authority outcome.
697
706
 
707
+ 60. **Runtime binding is proved, never inferred.** A platform adapter adopts
708
+ an existing runtime only when Shell's published loopback address answers
709
+ its authenticated health probe for the selected user and runtime label. A
710
+ durable ready registration with no answering runtime may be resumed; an
711
+ absent or incomplete registration requires login. A UI marker, PID, open
712
+ port, or installed executable alone is never service readiness.
713
+
714
+ 61. **Browser is composed once and driven through capability ports.** Shell
715
+ owns the one `BrowserService` composition for a machine runtime. A fresh,
716
+ private Electron host may supply Browser's visible driver and isolated
717
+ cookie adapter; without that capability Browser selects headless. Shell
718
+ never implements a second browser action, registry, profile, cookie, or
719
+ authentication authority, and a Browser session never changes backend. A
720
+ signed machine release that does not declare Browser must not instantiate
721
+ Browser state merely because the SDK is installed.
722
+
723
+ 62. **PTY is one authenticated Shell host capability.** The running machine
724
+ runtime mounts the Core-owned PTY contract through Shell's native process
725
+ adapter and closes every spawned process with that runtime. A UI targeting
726
+ this computer connects through the published authenticated loopback
727
+ binding; only a different computer uses the remote relay. Electron never
728
+ creates a second PTY implementation. A session with no explicit working
729
+ directory starts at that signed-in user's portable Amalgm root.
730
+
731
+ 63. **Chat has one execution path.** Agents resolves an installed identity and
732
+ exact immutable revision; Tools resolves the selected catalog revision;
733
+ Chat owns the conversation, provider pump, journal, reconnect, usage, and
734
+ interrupt. Shell mounts Chat's public HTTP adapter in both `bootShell` and
735
+ the signed `MachineHost`; it owns no session store, event translation, or
736
+ callback driver.
737
+
738
+ 64. **One installation device key and broker serve every local product.**
739
+ Before registration, Shell creates one P-256 private key for the physical
740
+ installation, binds its public thumbprint into the approved setup intent,
741
+ and proves possession before pinning that thumbprint across the computer's
742
+ runtime labels. The key stays in macOS Keychain or a Windows DPAPI envelope
743
+ where available (or one mode-0600 fallback file). A registered runtime
744
+ treats missing or corrupt key material as a visible failure; it never
745
+ generates a replacement during startup. Replacement is an explicit,
746
+ browser-approved recovery: Shell stages one candidate, proves its private
747
+ key, reuses it across uncertain retries, and promotes it only after the
748
+ control plane conditionally accepts the server-derived prior thumbprint.
749
+ Shell mints short-lived grants through Core's broker and creates a fresh
750
+ DPoP proof for each request. Chat, Apps, and future products never receive
751
+ the key or durable machine credential.
752
+
753
+ 65. **A Shell release is tested as the artifact users install.** The release
754
+ gate packs Shell, installs that tarball with the exact public Amalgm
755
+ dependency versions declared by its manifest, and exercises authorization
756
+ through Chat using package exports only. Sibling repositories and source
757
+ paths are never an integration-test dependency.
758
+
698
759
  ## Relationship to amalgm-engine
699
760
 
700
761
  Stage 1 cut the bricks out of the engine. Stage 2 proves them here, composed.
701
762
  Stage 3 ships each signed service from this shell and retires its Engine copy.
702
- The first production release is the closed Core + Files login/registration
703
- slice; later services cross the same parity gate without creating a second
704
- runtime for anything already released.
763
+ The current signed release is Core + Files + Agents + Tools + Chat. Later
764
+ services cross the same parity gate without creating a second runtime for
765
+ anything already released.
package/README.md CHANGED
@@ -1,18 +1,22 @@
1
1
  # @amalgm/shell
2
2
 
3
- The Amalgm runtime, assembled from published `@amalgm/*` SDK bricks instead of
4
- the monolith. This repo is the composition and parity ground for the
5
- lego-driven rewrite of `amalgm-engine` — see [PURPOSE.md](PURPOSE.md) for the
6
- axioms.
3
+ The Amalgm runtime, assembled from published `@amalgm/*` SDK capabilities.
4
+ This repository owns their host composition; see [PURPOSE.md](PURPOSE.md) for
5
+ the axioms.
7
6
 
8
- The first signed production release is intentionally Core + Files: Core owns
9
- machine login and readiness, Live owns the portable entity graph, and Shell
10
- injects the real filesystem, SQLite, watcher, and authenticated cloud wire.
7
+ The signed runtime composes Core + Files + Agents + Tools + Chat. Agents owns
8
+ identity and immutable revision resolution; Chat alone owns durable
9
+ conversations, turns, reconnect, and interrupt; Shell supplies the host effects
10
+ and one authenticated `/chat` protocol.
11
11
 
12
12
  ## Layout
13
13
 
14
14
  - `src/boot.ts` — assembles the HTTP/MCP runtime from bricks.
15
15
  - `src/machine-host.ts` — production effects injected into Core's machine setup.
16
+ - `src/runtime-command-host.ts` — authenticated loopback publication and the
17
+ read-only runtime-health contract used by platform adapters.
18
+ - `src/authorization/` — the installation device key, UI issuance adapter, and
19
+ Core credential-broker composition shared by local products.
16
20
  - `src/user-ground-host.ts` — production effects injected into Live registration.
17
21
  - `src/wire-client.ts` — the single authenticated gateway control path.
18
22
  - `src/content-direct.ts` — authorized direct-R2 download and local envelope opening.
@@ -20,12 +24,24 @@ injects the real filesystem, SQLite, watcher, and authenticated cloud wire.
20
24
  health, and retry; portable Watch law remains in `@amalgm/live`.
21
25
  - `tests/` — parity, adapter, restart, watcher, and two-device examinations.
22
26
 
27
+ Authorization issuers must be HTTPS origins outside loopback development,
28
+ credential-bearing redirects are rejected, and missing or unreadable stored
29
+ key material fails closed rather than silently rotating a server-pinned
30
+ identity. Recovery stages a separate candidate, requires browser approval and
31
+ candidate-key proof, and promotes only the server-accepted key.
32
+ Consumers use the contained `@amalgm/shell/authorization` export; importing
33
+ authorization does not load unrelated native runtime adapters.
34
+
23
35
  ## Run
24
36
 
25
37
  ```bash
26
38
  npm install
27
39
  npm test
28
40
 
41
+ # Pack Shell, install its exact released Amalgm dependencies in isolation,
42
+ # and exercise issuance, renewal, revocation, Chat egress, and key recovery.
43
+ npm run test:released-package
44
+
29
45
  # Isolated real-kernel Watch certification (macOS/Linux/Windows)
30
46
  AMALGM_NATIVE_WATCH=1 node --test tests/watching/native.test.js
31
47
 
@@ -37,6 +53,12 @@ npx --package @amalgm/shell amalgm login --no-open
37
53
  # Resume one user on a machine that has several
38
54
  npx --package @amalgm/shell amalgm run --user person@example.com
39
55
 
56
+ # Inspect registration and authenticated runtime health without starting either
57
+ npx --package @amalgm/shell amalgm status --user person@example.com
58
+
59
+ # Explicitly recover a missing, corrupt, or server-rejected device key
60
+ npx --package @amalgm/shell amalgm auth recover --user person@example.com
61
+
40
62
  # In another shell, declare an external workspace; the running Watch performs registration
41
63
  npx --package @amalgm/shell amalgm files register ./project --user person@example.com
42
64
 
@@ -46,6 +68,15 @@ npx --package @amalgm/shell amalgm files add <workspace-id> --user person@exampl
46
68
 
47
69
  Requires Node ≥ 20.19 (native `require(esm)` support; all bricks are ESM).
48
70
 
71
+ Package releases use `.github/workflows/publish-package.yml`. A reviewed main
72
+ commit is tagged exactly `v<package version>`; trusted npm publishing stages
73
+ that package, and a maintainer approves the staged artifact with 2FA. Manual
74
+ dirty-worktree publishing is not a release path. The release workflow also
75
+ installs the packed Shell in a clean temporary project and runs the public
76
+ authorization/Chat E2E against the exact registry versions in `package.json`.
77
+ The published `npm-shrinkwrap.json` fixes the complete dependency tree, and
78
+ that examination rejects any installed direct dependency that differs from it.
79
+
49
80
  Every push to `main` and every pull request runs the complete suite plus the
50
81
  isolated real-kernel Watch examination on GitHub-hosted Linux, macOS, and
51
82
  Windows machines. This certifies the same create, change, rename, move, delete,
@@ -0,0 +1,19 @@
1
+ import { type JsonWebKey } from "node:crypto";
2
+ import { type DpopSignerPort } from "@amalgm/core/authorization";
3
+ import type { DeviceKeyStore, StoredDeviceKey } from "./key-store.js";
4
+ export interface DeviceKey extends DpopSignerPort {
5
+ readonly publicJwk: JsonWebKey;
6
+ }
7
+ export declare function loadDeviceKey(store: DeviceKeyStore, ports?: Readonly<{
8
+ nowMs?: () => number;
9
+ randomUuid?: () => string;
10
+ }>): Promise<DeviceKey>;
11
+ export declare function requireDeviceKey(store: DeviceKeyStore, ports?: Readonly<{
12
+ nowMs?: () => number;
13
+ randomUuid?: () => string;
14
+ }>): Promise<DeviceKey>;
15
+ export declare function generateStoredDeviceKey(): StoredDeviceKey;
16
+ export declare function deviceKeyFromStored(stored: StoredDeviceKey, ports: Readonly<{
17
+ nowMs?: () => number;
18
+ randomUuid?: () => string;
19
+ }>): DeviceKey;
@@ -0,0 +1,81 @@
1
+ import { createHash, createPrivateKey, generateKeyPairSync, randomUUID, sign, } from "node:crypto";
2
+ import { normalizeDpopHtu, normalizeDpopMethod, } from "@amalgm/core/authorization";
3
+ export async function loadDeviceKey(store, ports = {}) {
4
+ let stored = await store.load();
5
+ if (!stored) {
6
+ stored = generateStoredDeviceKey();
7
+ await store.save(stored);
8
+ }
9
+ return deviceKeyFromStored(stored, ports);
10
+ }
11
+ export async function requireDeviceKey(store, ports = {}) {
12
+ const stored = await store.load();
13
+ if (!stored) {
14
+ throw Object.assign(new Error("Stored authorization key is missing"), {
15
+ code: "authorization_key_missing",
16
+ });
17
+ }
18
+ return deviceKeyFromStored(stored, ports);
19
+ }
20
+ export function generateStoredDeviceKey() {
21
+ const { privateKey } = generateKeyPairSync("ec", { namedCurve: "P-256" });
22
+ return Object.freeze({
23
+ version: 1,
24
+ privateJwk: privateKey.export({ format: "jwk" }),
25
+ });
26
+ }
27
+ export function deviceKeyFromStored(stored, ports) {
28
+ const privateJwk = stored.privateJwk;
29
+ if (stored.version !== 1
30
+ || privateJwk.kty !== "EC"
31
+ || privateJwk.crv !== "P-256"
32
+ || !privateJwk.x
33
+ || !privateJwk.y
34
+ || !privateJwk.d) {
35
+ throw new Error("Stored authorization key is not a P-256 private JWK");
36
+ }
37
+ const publicJwk = Object.freeze({
38
+ kty: "EC",
39
+ crv: "P-256",
40
+ x: privateJwk.x,
41
+ y: privateJwk.y,
42
+ });
43
+ const keyThumbprint = base64url(createHash("sha256").update(JSON.stringify({
44
+ crv: publicJwk.crv,
45
+ kty: publicJwk.kty,
46
+ x: publicJwk.x,
47
+ y: publicJwk.y,
48
+ })).digest());
49
+ const privateKey = createPrivateKey({ key: privateJwk, format: "jwk" });
50
+ return Object.freeze({
51
+ publicJwk,
52
+ keyThumbprint,
53
+ signProof: async (input) => {
54
+ const header = base64url(JSON.stringify({
55
+ typ: "dpop+jwt",
56
+ alg: "ES256",
57
+ jwk: publicJwk,
58
+ }));
59
+ const payload = base64url(JSON.stringify({
60
+ htm: normalizeDpopMethod(input.method),
61
+ htu: normalizeDpopHtu(input.url),
62
+ iat: Math.floor((ports.nowMs?.() ?? Date.now()) / 1000),
63
+ jti: ports.randomUuid?.() ?? randomUUID(),
64
+ ...(input.accessToken ? { ath: tokenHash(input.accessToken) } : {}),
65
+ }));
66
+ const signingInput = `${header}.${payload}`;
67
+ const signature = sign("sha256", Buffer.from(signingInput), {
68
+ key: privateKey,
69
+ dsaEncoding: "ieee-p1363",
70
+ });
71
+ return `${signingInput}.${base64url(signature)}`;
72
+ },
73
+ });
74
+ }
75
+ function tokenHash(token) {
76
+ return base64url(createHash("sha256").update(token).digest());
77
+ }
78
+ function base64url(value) {
79
+ return Buffer.from(value).toString("base64url");
80
+ }
81
+ //# sourceMappingURL=device-key.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-key.js","sourceRoot":"","sources":["../../src/authorization/device-key.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import { CredentialBroker } from "@amalgm/core/authorization";
2
+ import type { DeviceKeyStore } from "./key-store.js";
3
+ import { type DeviceKeyRecoveryStores } from "./recovery.js";
4
+ export { requestAuthorizationKeyRecovery } from "./recovery-client.js";
5
+ export type { AuthorizationRecoveryClientOptions } from "./recovery-client.js";
6
+ export interface ShellAuthorizationOptions {
7
+ /** Installation-scoped Shell state directory, shared by every local product. */
8
+ readonly stateDir: string;
9
+ readonly appUrl: string;
10
+ readonly computerAuthToken: string;
11
+ readonly computerId: string;
12
+ readonly deviceId: string;
13
+ readonly fetch?: typeof globalThis.fetch;
14
+ readonly platform?: NodeJS.Platform;
15
+ readonly keyStore?: DeviceKeyStore;
16
+ readonly nowMs?: () => number;
17
+ readonly random?: () => number;
18
+ }
19
+ export declare function loadInstallationDeviceKey(options: Readonly<{
20
+ stateDir: string;
21
+ deviceId: string;
22
+ platform?: NodeJS.Platform;
23
+ keyStore?: DeviceKeyStore;
24
+ nowMs?: () => number;
25
+ }>): Promise<import("./device-key.js").DeviceKey>;
26
+ export declare function requireInstallationDeviceKey(options: Readonly<{
27
+ stateDir: string;
28
+ deviceId: string;
29
+ platform?: NodeJS.Platform;
30
+ keyStore?: DeviceKeyStore;
31
+ nowMs?: () => number;
32
+ }>): Promise<import("./device-key.js").DeviceKey>;
33
+ export declare function installationDeviceKeyStores(options: Readonly<{
34
+ stateDir: string;
35
+ deviceId: string;
36
+ platform?: NodeJS.Platform;
37
+ keyStore?: DeviceKeyStore;
38
+ recoveryKeyStore?: DeviceKeyStore;
39
+ }>): DeviceKeyRecoveryStores;
40
+ export declare function stageInstallationDeviceKeyRecovery(options: Readonly<{
41
+ stateDir: string;
42
+ deviceId: string;
43
+ platform?: NodeJS.Platform;
44
+ keyStore?: DeviceKeyStore;
45
+ recoveryKeyStore?: DeviceKeyStore;
46
+ nowMs?: () => number;
47
+ }>): Promise<import("./device-key.js").DeviceKey>;
48
+ export declare function promoteInstallationDeviceKeyRecovery(options: Readonly<{
49
+ stateDir: string;
50
+ deviceId: string;
51
+ acceptedKeyThumbprint: string;
52
+ platform?: NodeJS.Platform;
53
+ keyStore?: DeviceKeyStore;
54
+ recoveryKeyStore?: DeviceKeyStore;
55
+ }>): Promise<import("./device-key.js").DeviceKey>;
56
+ /** Compose one key and one live broker for every local product in this runtime. */
57
+ export declare function createShellAuthorization(options: ShellAuthorizationOptions): Promise<CredentialBroker>;
58
+ export { deviceKeyFromStored, generateStoredDeviceKey, loadDeviceKey, requireDeviceKey, } from "./device-key.js";
59
+ export type { DeviceKey } from "./device-key.js";
60
+ export { createDeviceKeyStore } from "./key-store.js";
61
+ export type { DeviceKeyStore, StoredDeviceKey } from "./key-store.js";
62
+ export { promoteDeviceKeyRecovery, stageDeviceKeyRecovery } from "./recovery.js";
63
+ export type { DeviceKeyRecoveryStores } from "./recovery.js";
64
+ export { createShellCredentialIssuer } from "./issuer.js";
65
+ export type { ShellCredentialIssuerOptions } from "./issuer.js";
@@ -0,0 +1,63 @@
1
+ import { join } from "node:path";
2
+ import { CredentialBroker } from "@amalgm/core/authorization";
3
+ import { loadDeviceKey, requireDeviceKey } from "./device-key.js";
4
+ import { createShellCredentialIssuer } from "./issuer.js";
5
+ import { createDeviceKeyStore } from "./key-store.js";
6
+ import { promoteDeviceKeyRecovery, stageDeviceKeyRecovery, } from "./recovery.js";
7
+ export { requestAuthorizationKeyRecovery } from "./recovery-client.js";
8
+ export async function loadInstallationDeviceKey(options) {
9
+ const store = options.keyStore ?? createDeviceKeyStore({
10
+ file: join(options.stateDir, "authorization", "device-key.json"),
11
+ account: `device:${options.deviceId}`,
12
+ ...(options.platform ? { platform: options.platform } : {}),
13
+ });
14
+ return loadDeviceKey(store, { nowMs: options.nowMs });
15
+ }
16
+ export async function requireInstallationDeviceKey(options) {
17
+ const stores = installationDeviceKeyStores(options);
18
+ return requireDeviceKey(stores.active, { nowMs: options.nowMs });
19
+ }
20
+ export function installationDeviceKeyStores(options) {
21
+ const platform = options.platform ? { platform: options.platform } : {};
22
+ return Object.freeze({
23
+ active: options.keyStore ?? createDeviceKeyStore({
24
+ file: join(options.stateDir, "authorization", "device-key.json"),
25
+ account: `device:${options.deviceId}`,
26
+ ...platform,
27
+ }),
28
+ pending: options.recoveryKeyStore ?? createDeviceKeyStore({
29
+ file: join(options.stateDir, "authorization", "device-key-recovery.json"),
30
+ account: `device:${options.deviceId}:recovery`,
31
+ ...platform,
32
+ }),
33
+ });
34
+ }
35
+ export async function stageInstallationDeviceKeyRecovery(options) {
36
+ return stageDeviceKeyRecovery(installationDeviceKeyStores(options), { nowMs: options.nowMs });
37
+ }
38
+ export async function promoteInstallationDeviceKeyRecovery(options) {
39
+ return promoteDeviceKeyRecovery(installationDeviceKeyStores(options), options.acceptedKeyThumbprint);
40
+ }
41
+ /** Compose one key and one live broker for every local product in this runtime. */
42
+ export async function createShellAuthorization(options) {
43
+ const signer = await requireInstallationDeviceKey(options);
44
+ const issuer = createShellCredentialIssuer({
45
+ appUrl: options.appUrl,
46
+ computerAuthToken: options.computerAuthToken,
47
+ computerId: options.computerId,
48
+ deviceId: options.deviceId,
49
+ signer,
50
+ ...(options.fetch ? { fetch: options.fetch } : {}),
51
+ });
52
+ return new CredentialBroker({
53
+ issuer,
54
+ signer,
55
+ nowMs: options.nowMs ?? (() => Date.now()),
56
+ random: options.random ?? (() => Math.random()),
57
+ });
58
+ }
59
+ export { deviceKeyFromStored, generateStoredDeviceKey, loadDeviceKey, requireDeviceKey, } from "./device-key.js";
60
+ export { createDeviceKeyStore } from "./key-store.js";
61
+ export { promoteDeviceKeyRecovery, stageDeviceKeyRecovery } from "./recovery.js";
62
+ export { createShellCredentialIssuer } from "./issuer.js";
63
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/authorization/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAE9D,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAClE,OAAO,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEtD,OAAO,EACL,wBAAwB,EACxB,sBAAsB,GAEvB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,+BAA+B,EAAE,MAAM,sBAAsB,CAAC;AAiBvE,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,OAM9C;IACA,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,IAAI,oBAAoB,CAAC;QACrD,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,eAAe,EAAE,iBAAiB,CAAC;QAChE,OAAO,EAAE,UAAU,OAAO,CAAC,QAAQ,EAAE;QACrC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC5D,CAAC,CAAC;IACH,OAAO,aAAa,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAAC,OAMjD;IACA,MAAM,MAAM,GAAG,2BAA2B,CAAC,OAAO,CAAC,CAAC;IACpD,OAAO,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,OAM1C;IACA,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACxE,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,MAAM,EAAE,OAAO,CAAC,QAAQ,IAAI,oBAAoB,CAAC;YAC/C,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,eAAe,EAAE,iBAAiB,CAAC;YAChE,OAAO,EAAE,UAAU,OAAO,CAAC,QAAQ,EAAE;YACrC,GAAG,QAAQ;SACZ,CAAC;QACF,OAAO,EAAE,OAAO,CAAC,gBAAgB,IAAI,oBAAoB,CAAC;YACxD,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,eAAe,EAAE,0BAA0B,CAAC;YACzE,OAAO,EAAE,UAAU,OAAO,CAAC,QAAQ,WAAW;YAC9C,GAAG,QAAQ;SACZ,CAAC;KACH,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kCAAkC,CAAC,OAOvD;IACA,OAAO,sBAAsB,CAAC,2BAA2B,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;AAChG,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oCAAoC,CACxD,OAOE;IAEF,OAAO,wBAAwB,CAC7B,2BAA2B,CAAC,OAAO,CAAC,EACpC,OAAO,CAAC,qBAAqB,CAC9B,CAAC;AACJ,CAAC;AAED,mFAAmF;AACnF,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,OAAkC;IAC/E,MAAM,MAAM,GAAG,MAAM,4BAA4B,CAAC,OAAO,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,2BAA2B,CAAC;QACzC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,iBAAiB,EAAE,OAAO,CAAC,iBAAiB;QAC5C,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,MAAM;QACN,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACnD,CAAC,CAAC;IACH,OAAO,IAAI,gBAAgB,CAAC;QAC1B,MAAM;QACN,MAAM;QACN,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;QAC1C,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;KAChD,CAAC,CAAC;AACL,CAAC;AAED,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,aAAa,EACb,gBAAgB,GACjB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEtD,OAAO,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAEjF,OAAO,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { CredentialIssuerPort, DpopSignerPort } from "@amalgm/core/authorization";
2
+ export interface ShellCredentialIssuerOptions {
3
+ readonly appUrl: string;
4
+ readonly computerAuthToken: string;
5
+ readonly computerId: string;
6
+ readonly deviceId: string;
7
+ readonly clientId?: string;
8
+ readonly signer: DpopSignerPort;
9
+ readonly fetch?: typeof globalThis.fetch;
10
+ }
11
+ export declare function createShellCredentialIssuer(options: ShellCredentialIssuerOptions): CredentialIssuerPort;
@@ -0,0 +1,76 @@
1
+ export function createShellCredentialIssuer(options) {
2
+ const endpoint = `${authorizationOrigin(options.appUrl)}/api/authorization/v1/tokens`;
3
+ const request = options.fetch ?? globalThis.fetch;
4
+ return {
5
+ issue: async (grant) => {
6
+ const proof = await options.signer.signProof({ method: "POST", url: endpoint });
7
+ const response = await request(endpoint, {
8
+ method: "POST",
9
+ headers: {
10
+ authorization: `Bearer ${options.computerAuthToken}`,
11
+ "content-type": "application/json",
12
+ dpop: proof,
13
+ },
14
+ redirect: "error",
15
+ body: JSON.stringify({
16
+ computer_id: options.computerId,
17
+ device_id: options.deviceId,
18
+ client_id: options.clientId ?? "amalgm-shell",
19
+ audience: grant.audience,
20
+ scopes: grant.scopes,
21
+ }),
22
+ });
23
+ const body = await response.json().catch(() => ({}));
24
+ if (!response.ok) {
25
+ throw Object.assign(new Error(text(body.error) || "Access credential issuance failed"), {
26
+ code: text(body.code) || `authorization_http_${response.status}`,
27
+ status: response.status,
28
+ });
29
+ }
30
+ return issuedCredential(body, grant.keyThumbprint);
31
+ },
32
+ };
33
+ }
34
+ function authorizationOrigin(value) {
35
+ let url;
36
+ try {
37
+ url = new URL(value);
38
+ }
39
+ catch {
40
+ throw new TypeError("appUrl must be an HTTPS origin");
41
+ }
42
+ const local = url.hostname === "localhost" || url.hostname === "127.0.0.1";
43
+ if ((url.protocol !== "https:" && !(local && url.protocol === "http:"))
44
+ || url.username
45
+ || url.password
46
+ || url.search
47
+ || url.hash
48
+ || url.pathname !== "/")
49
+ throw new TypeError("appUrl must be an HTTPS origin");
50
+ return url.origin;
51
+ }
52
+ function issuedCredential(body, keyThumbprint) {
53
+ const accessToken = text(body.access_token);
54
+ const audience = text(body.audience);
55
+ const scopes = Array.isArray(body.scopes)
56
+ ? body.scopes.filter((scope) => typeof scope === "string")
57
+ : [];
58
+ const expiresAtMs = Date.parse(text(body.expires_at));
59
+ if (!accessToken || body.token_type !== "DPoP" || !audience || scopes.length === 0 || !Number.isFinite(expiresAtMs)) {
60
+ throw Object.assign(new Error("Authorization server returned an invalid credential"), {
61
+ code: "invalid_authorization_response",
62
+ });
63
+ }
64
+ return Object.freeze({
65
+ accessToken,
66
+ tokenType: "DPoP",
67
+ audience,
68
+ scopes: Object.freeze(scopes),
69
+ expiresAtMs,
70
+ keyThumbprint,
71
+ });
72
+ }
73
+ function text(value) {
74
+ return typeof value === "string" ? value.trim() : "";
75
+ }
76
+ //# sourceMappingURL=issuer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issuer.js","sourceRoot":"","sources":["../../src/authorization/issuer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { JsonWebKey } from "node:crypto";
2
+ export interface StoredDeviceKey {
3
+ readonly version: 1;
4
+ readonly privateJwk: JsonWebKey;
5
+ }
6
+ export interface DeviceKeyStore {
7
+ load(): Promise<StoredDeviceKey | null>;
8
+ save(key: StoredDeviceKey): Promise<void>;
9
+ remove(): Promise<void>;
10
+ }
11
+ export declare function createDeviceKeyStore(input: Readonly<{
12
+ file: string;
13
+ account: string;
14
+ platform?: NodeJS.Platform;
15
+ }>): DeviceKeyStore;