@descryy/runtime-environment-profile 0.2.0 → 0.4.0

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.
package/LICENSE ADDED
@@ -0,0 +1,6 @@
1
+ Copyright (c) Descry
2
+
3
+ All rights reserved. This software is proprietary and confidential.
4
+ No license, express or implied, to reproduce, distribute, modify, or
5
+ create derivative works is granted except as explicitly agreed in
6
+ writing.
@@ -1,31 +1,14 @@
1
1
  /**
2
- * Credential storage — DEC-270: "Credentials stored locally, in the OS
3
- * keychain on the user's machine — not on a Descry server." This repo has
4
- * no server component (`descry-core` CLAUDE.md, "Repo split"), so that
5
- * guarantee is entirely a local-storage concern here.
6
- *
2
+ * DEC-270: credentials stored locally (OS keychain), never on a server.
7
3
  * `CredentialStore` is the seam a real OS-keychain backend (`keytar` or
8
- * equivalent) plugs into later. **Chosen for this pass:
9
- * `EncryptedFileCredentialStore`, an at-rest AES-256-GCM file store, not a
10
- * real keychain integration** — disclosed per the "honest degradation"
11
- * rule (`descry-core` CLAUDE.md rule 7), not silently passed off as the
12
- * real thing:
13
- *
14
- * - Neither this repo's root `package.json` nor `descry-core`'s already
15
- * depends on `keytar` or any keychain wrapper (checked before writing
16
- * this file), and `keytar` itself is an unmaintained native module
17
- * (prebuilt binaries, no upstream releases since 2021) — exactly the
18
- * "heavy new dependency added without checking existing ones" this
19
- * pass was told to avoid, for a package that would need per-platform
20
- * native builds to even test in CI.
21
- * - `node:crypto` is already a dependency of this repo (`randomUUID` is
22
- * used in `backend-observation`, `browser`, `controller`,
23
- * `evidence-store`) and needs no native build step, so the interim
24
- * implementation is buildable and testable everywhere `node --test`
25
- * already runs.
26
- * - The interface below does not leak this choice: a future
27
- * `KeychainCredentialStore` satisfies the same three methods and every
28
- * caller (including the safety gate) is unaffected by the swap.
4
+ * equivalent) plugs into later. This pass uses `EncryptedFileCredentialStore`
5
+ * (AES-256-GCM file store) instead, disclosed per honest-degradation
6
+ * (rule 7) rather than passed off as a real keychain:
7
+ * - `keytar` is unmaintained (no releases since 2021, native prebuilt
8
+ * binaries) and neither this repo nor `descry-core` already depends on it.
9
+ * - `node:crypto` is already used elsewhere in this repo, no native build.
10
+ * - The interface doesn't leak this choice — a future
11
+ * `KeychainCredentialStore` is a drop-in swap.
29
12
  */
30
13
  export interface CredentialStore {
31
14
  /** Stores `secret` under `credentialRef`, overwriting any existing value for that ref. */
@@ -36,22 +19,15 @@ export interface CredentialStore {
36
19
  delete(credentialRef: string): Promise<void>;
37
20
  }
38
21
  /**
39
- * AES-256-GCM file-at-rest fallback. One file per `credentialRef`, named
40
- * by the SHA-256 hash of the ref rather than the ref itself — so scoping
41
- * is enforced structurally (a lookup under a different ref hashes to a
42
- * different filename and simply finds nothing) rather than only by
43
- * convention, and so a ref containing characters unsafe for a filename
44
- * never needs special-casing.
22
+ * AES-256-GCM file-at-rest fallback. Each `credentialRef` is stored under
23
+ * the SHA-256 hash of the ref, not the ref itself — scoping is structural
24
+ * (wrong ref hashes to a different, absent file) and unsafe filename
25
+ * characters never need special-casing.
45
26
  *
46
- * The AES key is a 32-byte value generated once and written to
47
- * `master.key` inside the store directory, `chmod`'d to `0o600`
48
- * (owner-only) immediately after write — the directory itself is created
49
- * `0o700`. This protects against other *users* on a shared machine, not
50
- * against another *process running as the same user*, which is the
51
- * limitation a real OS keychain (backed by the platform's own access
52
- * control / user login secret) does not have. That gap is exactly why
53
- * this is documented as an interim fallback rather than the intended
54
- * long-term implementation.
27
+ * The AES key lives in `master.key` under the store dir, both chmod'd
28
+ * 0o600/0o700 owner-only. This protects against other users on a shared
29
+ * machine, not another process running as the same user — a real OS
30
+ * keychain doesn't have that gap, which is why this is interim.
55
31
  */
56
32
  export declare class EncryptedFileCredentialStore implements CredentialStore {
57
33
  private readonly baseDir;
@@ -1 +1 @@
1
- {"version":3,"file":"credential-store.d.ts","sourceRoot":"","sources":["../src/credential-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAQH,MAAM,WAAW,eAAe;IAC9B,0FAA0F;IAC1F,KAAK,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,kGAAkG;IAClG,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACxD,mGAAmG;IACnG,MAAM,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9C;AAYD;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,4BAA6B,YAAW,eAAe;IAClE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,UAAU,CAAgC;gBAEtC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE;IAM7C,KAAK,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB3D,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAiBvD,MAAM,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWlD,OAAO,CAAC,SAAS;YAKH,eAAe;YAOf,uBAAuB;CAUtC"}
1
+ {"version":3,"file":"credential-store.d.ts","sourceRoot":"","sources":["../src/credential-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAQH,MAAM,WAAW,eAAe;IAC9B,0FAA0F;IAC1F,KAAK,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,kGAAkG;IAClG,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACxD,mGAAmG;IACnG,MAAM,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9C;AAkBD;;;;;;;;;;GAUG;AACH,qBAAa,4BAA6B,YAAW,eAAe;IAClE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,UAAU,CAAgC;gBAEtC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE;IAM7C,KAAK,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB3D,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IA2BvD,MAAM,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWlD,OAAO,CAAC,SAAS;YAKH,eAAe;YAOf,uBAAuB;CAUtC"}
@@ -1,31 +1,14 @@
1
1
  /**
2
- * Credential storage — DEC-270: "Credentials stored locally, in the OS
3
- * keychain on the user's machine — not on a Descry server." This repo has
4
- * no server component (`descry-core` CLAUDE.md, "Repo split"), so that
5
- * guarantee is entirely a local-storage concern here.
6
- *
2
+ * DEC-270: credentials stored locally (OS keychain), never on a server.
7
3
  * `CredentialStore` is the seam a real OS-keychain backend (`keytar` or
8
- * equivalent) plugs into later. **Chosen for this pass:
9
- * `EncryptedFileCredentialStore`, an at-rest AES-256-GCM file store, not a
10
- * real keychain integration** — disclosed per the "honest degradation"
11
- * rule (`descry-core` CLAUDE.md rule 7), not silently passed off as the
12
- * real thing:
13
- *
14
- * - Neither this repo's root `package.json` nor `descry-core`'s already
15
- * depends on `keytar` or any keychain wrapper (checked before writing
16
- * this file), and `keytar` itself is an unmaintained native module
17
- * (prebuilt binaries, no upstream releases since 2021) — exactly the
18
- * "heavy new dependency added without checking existing ones" this
19
- * pass was told to avoid, for a package that would need per-platform
20
- * native builds to even test in CI.
21
- * - `node:crypto` is already a dependency of this repo (`randomUUID` is
22
- * used in `backend-observation`, `browser`, `controller`,
23
- * `evidence-store`) and needs no native build step, so the interim
24
- * implementation is buildable and testable everywhere `node --test`
25
- * already runs.
26
- * - The interface below does not leak this choice: a future
27
- * `KeychainCredentialStore` satisfies the same three methods and every
28
- * caller (including the safety gate) is unaffected by the swap.
4
+ * equivalent) plugs into later. This pass uses `EncryptedFileCredentialStore`
5
+ * (AES-256-GCM file store) instead, disclosed per honest-degradation
6
+ * (rule 7) rather than passed off as a real keychain:
7
+ * - `keytar` is unmaintained (no releases since 2021, native prebuilt
8
+ * binaries) and neither this repo nor `descry-core` already depends on it.
9
+ * - `node:crypto` is already used elsewhere in this repo, no native build.
10
+ * - The interface doesn't leak this choice — a future
11
+ * `KeychainCredentialStore` is a drop-in swap.
29
12
  */
30
13
  import { randomBytes, createCipheriv, createDecipheriv, createHash } from "node:crypto";
31
14
  import { mkdir, readFile, writeFile, unlink, chmod } from "node:fs/promises";
@@ -35,23 +18,22 @@ import { join } from "node:path";
35
18
  const ALGORITHM = "aes-256-gcm";
36
19
  const KEY_BYTES = 32;
37
20
  const IV_BYTES = 12;
21
+ // Pinned to 16 bytes on both cipher directions (S-1): unpinned, `setAuthTag`
22
+ // accepts any shorter tag and GCM authenticates against only that many
23
+ // bytes (a real tag's 4/8/12-byte prefix all pass). Pinning here, plus the
24
+ // length check in retrieve() below, is what forces full 2^128 forgery
25
+ // resistance instead of whatever length is present on disk.
26
+ const AUTH_TAG_BYTES = 16;
38
27
  /**
39
- * AES-256-GCM file-at-rest fallback. One file per `credentialRef`, named
40
- * by the SHA-256 hash of the ref rather than the ref itself — so scoping
41
- * is enforced structurally (a lookup under a different ref hashes to a
42
- * different filename and simply finds nothing) rather than only by
43
- * convention, and so a ref containing characters unsafe for a filename
44
- * never needs special-casing.
28
+ * AES-256-GCM file-at-rest fallback. Each `credentialRef` is stored under
29
+ * the SHA-256 hash of the ref, not the ref itself — scoping is structural
30
+ * (wrong ref hashes to a different, absent file) and unsafe filename
31
+ * characters never need special-casing.
45
32
  *
46
- * The AES key is a 32-byte value generated once and written to
47
- * `master.key` inside the store directory, `chmod`'d to `0o600`
48
- * (owner-only) immediately after write — the directory itself is created
49
- * `0o700`. This protects against other *users* on a shared machine, not
50
- * against another *process running as the same user*, which is the
51
- * limitation a real OS keychain (backed by the platform's own access
52
- * control / user login secret) does not have. That gap is exactly why
53
- * this is documented as an interim fallback rather than the intended
54
- * long-term implementation.
33
+ * The AES key lives in `master.key` under the store dir, both chmod'd
34
+ * 0o600/0o700 owner-only. This protects against other users on a shared
35
+ * machine, not another process running as the same user — a real OS
36
+ * keychain doesn't have that gap, which is why this is interim.
55
37
  */
56
38
  export class EncryptedFileCredentialStore {
57
39
  baseDir;
@@ -67,7 +49,7 @@ export class EncryptedFileCredentialStore {
67
49
  await mkdir(this.entriesDir, { recursive: true, mode: 0o700 });
68
50
  const key = await this.loadOrCreateKey();
69
51
  const iv = randomBytes(IV_BYTES);
70
- const cipher = createCipheriv(ALGORITHM, key, iv);
52
+ const cipher = createCipheriv(ALGORITHM, key, iv, { authTagLength: AUTH_TAG_BYTES });
71
53
  const ciphertext = Buffer.concat([cipher.update(secret, "utf8"), cipher.final()]);
72
54
  const envelope = {
73
55
  iv: iv.toString("base64"),
@@ -86,8 +68,16 @@ export class EncryptedFileCredentialStore {
86
68
  const key = await this.loadOrCreateKey();
87
69
  const raw = await readFile(path, "utf8");
88
70
  const envelope = JSON.parse(raw);
89
- const decipher = createDecipheriv(ALGORITHM, key, Buffer.from(envelope.iv, "base64"));
90
- decipher.setAuthTag(Buffer.from(envelope.authTag, "base64"));
71
+ const authTag = Buffer.from(envelope.authTag, "base64");
72
+ // Reject on length before setAuthTag/final(): anything but a full
73
+ // 16-byte tag (truncated, or written by a pre-S-1 build) must fail
74
+ // loudly rather than risk authenticating against a shortened tag.
75
+ if (authTag.length !== AUTH_TAG_BYTES) {
76
+ throw new Error(`EncryptedFileCredentialStore: refusing to decrypt "${credentialRef}" -- authTag is ${String(authTag.length)} bytes, expected exactly ${String(AUTH_TAG_BYTES)}. ` +
77
+ "This envelope is either tampered with or was written before authTagLength was pinned (S-1); it cannot be safely decrypted as-is.");
78
+ }
79
+ const decipher = createDecipheriv(ALGORITHM, key, Buffer.from(envelope.iv, "base64"), { authTagLength: AUTH_TAG_BYTES });
80
+ decipher.setAuthTag(authTag);
91
81
  const plaintext = Buffer.concat([
92
82
  decipher.update(Buffer.from(envelope.ciphertext, "base64")),
93
83
  decipher.final(),
@@ -1 +1 @@
1
- {"version":3,"file":"credential-store.js","sourceRoot":"","sources":["../src/credential-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"credential-store.js","sourceRoot":"","sources":["../src/credential-store.ts"],"names":[],"mappings":"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"}
package/dist/profile.d.ts CHANGED
@@ -1,38 +1,24 @@
1
1
  /**
2
- * Environment profile — DEC-270 (`documents/decisions-inbox/
3
- * DEC-270-environment-profiles-declare-safety-not-infer-it.md`), building on
4
- * DEC-266's four target kinds (local booted, local attached, remote
5
- * dev/staging/preview, production).
2
+ * DEC-270, building on DEC-266's four target kinds (local booted, local
3
+ * attached, remote dev/staging/preview, production).
6
4
  *
7
- * A profile is a **user-named** target: no fixed vocabulary. "staging"
8
- * means different things at different companies, so nothing here
9
- * pattern-matches on `name` — the safety level below is the only thing
10
- * that governs what is permitted, and it is always set explicitly by the
11
- * user, never guessed from the name or the URL.
5
+ * A profile is user-named, no fixed vocabulary — nothing here pattern-matches
6
+ * on `name`; `safetyLevel` alone governs what's permitted, always set
7
+ * explicitly by the user, never guessed from name or URL.
12
8
  *
13
- * `credentialRef` is a reference into a `CredentialStore` (see
14
- * `credential-store.ts`), never the raw secret — this type is safe to log,
15
- * serialize or hand to a planner without ever exposing what it points at.
9
+ * `credentialRef` is a reference into a `CredentialStore`, never the raw
10
+ * secret — safe to log, serialize, or hand to a planner.
16
11
  *
17
- * `mode` and `remoteLogSource` are RT-197/DEC-271's addition. Until this
18
- * pass `EnvironmentProfile` had no field naming which of DEC-266's four
19
- * kinds a given profile actually was — `@descryy/runtime-remote-log-ingestion`
20
- * existed with real tests and zero callers, and there was no profile-level
21
- * place for a "remote/production" target to declare where its logs live,
22
- * so nothing could have called it. `mode` closes that: it is what
23
- * `packages/orchestrator/src/profile-backend-observation.ts` reads to
24
- * choose DEC-271's mechanism — a real `InboundProxy` for `"localBooted"`,
12
+ * `mode`/`remoteLogSource` (RT-197/DEC-271) name which of DEC-266's four
13
+ * kinds a profile is; `profile-backend-observation.ts` reads `mode` to pick
14
+ * DEC-271's mechanism — `InboundProxy` for `"localBooted"`,
25
15
  * `remote-log-ingestion` for `"remote"`/`"production"`, never both.
26
16
  */
27
17
  import type { RemoteLogSourceConfig } from "@descryy/runtime-remote-log-ingestion";
28
18
  /** Set once by the user per profile. Never inferred from `name` (DEC-270's core rule). */
29
19
  export declare const SAFETY_LEVELS: readonly ["readOnly", "write", "destructiveWithApproval"];
30
20
  export type SafetyLevel = (typeof SAFETY_LEVELS)[number];
31
- /**
32
- * DEC-266's four target kinds, named as a closed union for the first time
33
- * (RT-197). Set once by the user per profile, same discipline as
34
- * `safetyLevel` — never inferred from `name` or `url`.
35
- */
21
+ /** DEC-266's four target kinds, closed union (RT-197). Never inferred, like `safetyLevel`. */
36
22
  export declare const PROFILE_MODES: readonly ["localBooted", "localAttached", "remote", "production"];
37
23
  export type ProfileMode = (typeof PROFILE_MODES)[number];
38
24
  export interface EnvironmentProfile {
@@ -41,37 +27,29 @@ export interface EnvironmentProfile {
41
27
  readonly url: string;
42
28
  readonly safetyLevel: SafetyLevel;
43
29
  /**
44
- * Opaque key into a `CredentialStore` — never the raw secret itself.
45
- * Scoped to this profile only: two profiles must never share a
46
- * `credentialRef`, so a credential entered for one is never retrievable
47
- * under another's identity, even by accident (DEC-270).
30
+ * Opaque key into a `CredentialStore` — never the raw secret. Scoped per
31
+ * profile: two profiles must never share a `credentialRef` (DEC-270).
48
32
  */
49
33
  readonly credentialRef: string;
50
34
  /** Which of DEC-266's four kinds this profile targets. Governs DEC-271's mechanism choice — see this file's own header. */
51
35
  readonly mode: ProfileMode;
52
36
  /**
53
- * DEC-271's "ask, don't guess" input for the remote/production mechanism
54
- * — where the user says their logs live, per
55
- * `@descryy/runtime-remote-log-ingestion`'s `RemoteLogSourceConfig`. Only
56
- * meaningful for `mode: "remote"` or `mode: "production"`;
57
- * `validateProfile` rejects it on a `"localBooted"`/`"localAttached"`
58
- * profile rather than silently ignoring it, because a caller that set it
59
- * there believed it would be honored and it would not have been.
37
+ * DEC-271's "ask, don't guess" input for remote/production log location.
38
+ * Only meaningful for `mode: "remote"`/`"production"` — `validateProfile`
39
+ * rejects it on a local mode rather than silently ignoring it, since a
40
+ * caller that set it there expected it to be honored.
60
41
  *
61
42
  * `null`/absent on a remote or production profile is a real, honest
62
- * state — DEC-271's own "or a plain 'not observable here' when there's
63
- * nothing to work with" — not an error. This type has no dependency in
64
- * the other direction: `@descryy/runtime-remote-log-ingestion` does not
65
- * import from this package.
43
+ * state, not an error. No reverse dependency: `runtime-remote-log-ingestion`
44
+ * does not import from this package.
66
45
  */
67
46
  readonly remoteLogSource?: RemoteLogSourceConfig | null;
68
47
  }
69
48
  export type ProfileValidationError = "EMPTY_NAME" | "EMPTY_CREDENTIAL_REF" | "INVALID_URL" | "INVALID_SAFETY_LEVEL" | "INVALID_MODE" | "REMOTE_LOG_SOURCE_NOT_ALLOWED_FOR_MODE";
70
49
  /**
71
- * Structural validation only — no attempt to reach `url` or verify the
72
- * credential exists. Reachability and secret presence are runtime
73
- * concerns; this stays a pure check so it can run before either is
74
- * possible (e.g. while a setup form is still being filled in).
50
+ * Structural validation only — doesn't reach `url` or verify the
51
+ * credential exists (runtime concerns), so it can run while a setup form
52
+ * is still being filled in.
75
53
  */
76
54
  export declare function validateProfile(input: {
77
55
  readonly name: string;
@@ -81,12 +59,7 @@ export declare function validateProfile(input: {
81
59
  readonly mode: string;
82
60
  readonly remoteLogSource?: RemoteLogSourceConfig | null;
83
61
  }): readonly ProfileValidationError[];
84
- /**
85
- * Validates and constructs a profile in one step. Throws with every
86
- * validation error joined, rather than the first one only, so a caller
87
- * building a setup form doesn't have to fix and resubmit one field at a
88
- * time.
89
- */
62
+ /** Throws with every validation error joined, not just the first, so a setup form can show all problems at once. */
90
63
  export declare function createEnvironmentProfile(input: {
91
64
  readonly name: string;
92
65
  readonly url: string;
@@ -1 +1 @@
1
- {"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAEnF,0FAA0F;AAC1F,eAAO,MAAM,aAAa,2DAA4D,CAAC;AACvF,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD;;;;GAIG;AACH,eAAO,MAAM,aAAa,mEAAoE,CAAC;AAC/F,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,MAAM,WAAW,kBAAkB;IACjC,gGAAgG;IAChG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,2HAA2H;IAC3H,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAC;CACzD;AAED,MAAM,MAAM,sBAAsB,GAC9B,YAAY,GACZ,sBAAsB,GACtB,aAAa,GACb,sBAAsB,GACtB,cAAc,GACd,wCAAwC,CAAC;AAE7C;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,eAAe,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAC;CACzD,GAAG,SAAS,sBAAsB,EAAE,CA6BpC;AAWD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,eAAe,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAC;CACzD,GAAG,kBAAkB,CAarB"}
1
+ {"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAEnF,0FAA0F;AAC1F,eAAO,MAAM,aAAa,2DAA4D,CAAC;AACvF,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,8FAA8F;AAC9F,eAAO,MAAM,aAAa,mEAAoE,CAAC;AAC/F,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,MAAM,WAAW,kBAAkB;IACjC,gGAAgG;IAChG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC;;;OAGG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,2HAA2H;IAC3H,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B;;;;;;;;;OASG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAC;CACzD;AAED,MAAM,MAAM,sBAAsB,GAC9B,YAAY,GACZ,sBAAsB,GACtB,aAAa,GACb,sBAAsB,GACtB,cAAc,GACd,wCAAwC,CAAC;AAE7C;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,eAAe,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAC;CACzD,GAAG,SAAS,sBAAsB,EAAE,CA2BpC;AAWD,oHAAoH;AACpH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,eAAe,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAC;CACzD,GAAG,kBAAkB,CAarB"}
package/dist/profile.js CHANGED
@@ -1,42 +1,27 @@
1
1
  /**
2
- * Environment profile — DEC-270 (`documents/decisions-inbox/
3
- * DEC-270-environment-profiles-declare-safety-not-infer-it.md`), building on
4
- * DEC-266's four target kinds (local booted, local attached, remote
5
- * dev/staging/preview, production).
2
+ * DEC-270, building on DEC-266's four target kinds (local booted, local
3
+ * attached, remote dev/staging/preview, production).
6
4
  *
7
- * A profile is a **user-named** target: no fixed vocabulary. "staging"
8
- * means different things at different companies, so nothing here
9
- * pattern-matches on `name` — the safety level below is the only thing
10
- * that governs what is permitted, and it is always set explicitly by the
11
- * user, never guessed from the name or the URL.
5
+ * A profile is user-named, no fixed vocabulary — nothing here pattern-matches
6
+ * on `name`; `safetyLevel` alone governs what's permitted, always set
7
+ * explicitly by the user, never guessed from name or URL.
12
8
  *
13
- * `credentialRef` is a reference into a `CredentialStore` (see
14
- * `credential-store.ts`), never the raw secret — this type is safe to log,
15
- * serialize or hand to a planner without ever exposing what it points at.
9
+ * `credentialRef` is a reference into a `CredentialStore`, never the raw
10
+ * secret — safe to log, serialize, or hand to a planner.
16
11
  *
17
- * `mode` and `remoteLogSource` are RT-197/DEC-271's addition. Until this
18
- * pass `EnvironmentProfile` had no field naming which of DEC-266's four
19
- * kinds a given profile actually was — `@descryy/runtime-remote-log-ingestion`
20
- * existed with real tests and zero callers, and there was no profile-level
21
- * place for a "remote/production" target to declare where its logs live,
22
- * so nothing could have called it. `mode` closes that: it is what
23
- * `packages/orchestrator/src/profile-backend-observation.ts` reads to
24
- * choose DEC-271's mechanism — a real `InboundProxy` for `"localBooted"`,
12
+ * `mode`/`remoteLogSource` (RT-197/DEC-271) name which of DEC-266's four
13
+ * kinds a profile is; `profile-backend-observation.ts` reads `mode` to pick
14
+ * DEC-271's mechanism — `InboundProxy` for `"localBooted"`,
25
15
  * `remote-log-ingestion` for `"remote"`/`"production"`, never both.
26
16
  */
27
17
  /** Set once by the user per profile. Never inferred from `name` (DEC-270's core rule). */
28
18
  export const SAFETY_LEVELS = ["readOnly", "write", "destructiveWithApproval"];
29
- /**
30
- * DEC-266's four target kinds, named as a closed union for the first time
31
- * (RT-197). Set once by the user per profile, same discipline as
32
- * `safetyLevel` — never inferred from `name` or `url`.
33
- */
19
+ /** DEC-266's four target kinds, closed union (RT-197). Never inferred, like `safetyLevel`. */
34
20
  export const PROFILE_MODES = ["localBooted", "localAttached", "remote", "production"];
35
21
  /**
36
- * Structural validation only — no attempt to reach `url` or verify the
37
- * credential exists. Reachability and secret presence are runtime
38
- * concerns; this stays a pure check so it can run before either is
39
- * possible (e.g. while a setup form is still being filled in).
22
+ * Structural validation only — doesn't reach `url` or verify the
23
+ * credential exists (runtime concerns), so it can run while a setup form
24
+ * is still being filled in.
40
25
  */
41
26
  export function validateProfile(input) {
42
27
  const errors = [];
@@ -55,9 +40,7 @@ export function validateProfile(input) {
55
40
  if (!PROFILE_MODES.includes(input.mode)) {
56
41
  errors.push("INVALID_MODE");
57
42
  }
58
- // A log source declared on a mode DEC-271 never reads it for is a
59
- // silently-ignored setting, not a harmless extra -- rejected rather than
60
- // dropped, so the caller who set it learns it would not have been honored.
43
+ // Rejected rather than silently dropped -- caller learns it wouldn't be honored.
61
44
  if (input.remoteLogSource != null &&
62
45
  (input.mode === "localBooted" || input.mode === "localAttached")) {
63
46
  errors.push("REMOTE_LOG_SOURCE_NOT_ALLOWED_FOR_MODE");
@@ -73,12 +56,7 @@ function isParsableUrl(url) {
73
56
  return false;
74
57
  }
75
58
  }
76
- /**
77
- * Validates and constructs a profile in one step. Throws with every
78
- * validation error joined, rather than the first one only, so a caller
79
- * building a setup form doesn't have to fix and resubmit one field at a
80
- * time.
81
- */
59
+ /** Throws with every validation error joined, not just the first, so a setup form can show all problems at once. */
82
60
  export function createEnvironmentProfile(input) {
83
61
  const errors = validateProfile(input);
84
62
  if (errors.length > 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"profile.js","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,0FAA0F;AAC1F,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE,yBAAyB,CAAU,CAAC;AAGvF;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,aAAa,EAAE,eAAe,EAAE,QAAQ,EAAE,YAAY,CAAU,CAAC;AA2C/F;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,KAO/B;IACC,MAAM,MAAM,GAA6B,EAAE,CAAC;IAE5C,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC5B,CAAC;IACD,IAAI,KAAK,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5C,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC7B,CAAC;IACD,IAAI,CAAE,aAAmC,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC;QACtE,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,CAAE,aAAmC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/D,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC9B,CAAC;IACD,kEAAkE;IAClE,yEAAyE;IACzE,2EAA2E;IAC3E,IACE,KAAK,CAAC,eAAe,IAAI,IAAI;QAC7B,CAAC,KAAK,CAAC,IAAI,KAAK,aAAa,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe,CAAC,EAChE,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;IACxD,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,aAAa,CAAC,GAAW;IAChC,IAAI,CAAC;QACH,KAAK,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CAAC,KAOxC;IACC,MAAM,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;IACtC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,gCAAgC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvE,CAAC;IACD,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE;QACvB,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,aAAa,EAAE,KAAK,CAAC,aAAa,CAAC,IAAI,EAAE;QACzC,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,eAAe,EAAE,KAAK,CAAC,eAAe,IAAI,IAAI;KAC/C,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"profile.js","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,0FAA0F;AAC1F,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE,yBAAyB,CAAU,CAAC;AAGvF,8FAA8F;AAC9F,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,aAAa,EAAE,eAAe,EAAE,QAAQ,EAAE,YAAY,CAAU,CAAC;AAoC/F;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,KAO/B;IACC,MAAM,MAAM,GAA6B,EAAE,CAAC;IAE5C,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC5B,CAAC;IACD,IAAI,KAAK,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5C,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC7B,CAAC;IACD,IAAI,CAAE,aAAmC,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC;QACtE,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,CAAE,aAAmC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/D,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC9B,CAAC;IACD,iFAAiF;IACjF,IACE,KAAK,CAAC,eAAe,IAAI,IAAI;QAC7B,CAAC,KAAK,CAAC,IAAI,KAAK,aAAa,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe,CAAC,EAChE,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;IACxD,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,aAAa,CAAC,GAAW;IAChC,IAAI,CAAC;QACH,KAAK,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,oHAAoH;AACpH,MAAM,UAAU,wBAAwB,CAAC,KAOxC;IACC,MAAM,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;IACtC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,gCAAgC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvE,CAAC;IACD,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE;QACvB,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,aAAa,EAAE,KAAK,CAAC,aAAa,CAAC,IAAI,EAAE;QACzC,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,eAAe,EAAE,KAAK,CAAC,eAAe,IAAI,IAAI;KAC/C,CAAC;AACJ,CAAC"}
@@ -1,11 +1,8 @@
1
1
  /**
2
- * Safety gate — the enforcement point DEC-270 requires: "Any destructive
3
- * action checks the profile's declared level before running, not the
4
- * environment's name or the fact that it's 'not prod.'"
5
- *
6
- * This function does not wire itself into any caller — DEC-270 only asks
7
- * that it exist and be correct; the runners that actually perform
8
- * destructive actions (a different lane's work) call it before acting.
2
+ * DEC-270's enforcement point: destructive actions check the profile's
3
+ * declared safety level, never the environment's name or "not prod."
4
+ * Not wired to any caller here — runners that perform destructive actions
5
+ * (a different lane) call this before acting.
9
6
  */
10
7
  import type { EnvironmentProfile } from "./profile.ts";
11
8
  export declare const GATE_DECISIONS: readonly ["allow", "deny", "requiresApproval"];
@@ -29,10 +26,9 @@ export interface ProposedAction {
29
26
  * | `write` | allow | allow | deny |
30
27
  * | `destructiveWithApproval` | allow | allow | requiresApproval |
31
28
  *
32
- * `write` denies destructive rather than escalating it to approval — a
33
- * profile owner who chose `write` explicitly did not choose
34
- * `destructiveWithApproval`, and DEC-270's whole point is that the level
35
- * is declared, not inferred or auto-upgraded.
29
+ * `write` denies destructive rather than escalating to approval — an
30
+ * owner who chose `write` did not choose `destructiveWithApproval`;
31
+ * DEC-270 requires the level be declared, not auto-upgraded.
36
32
  */
37
33
  export declare function evaluateAction(profile: EnvironmentProfile, action: ProposedAction): GateDecision;
38
34
  //# sourceMappingURL=safety-gate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"safety-gate.d.ts","sourceRoot":"","sources":["../src/safety-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,eAAO,MAAM,cAAc,gDAAiD,CAAC;AAC7E,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D,MAAM,WAAW,cAAc;IAC7B,yFAAyF;IACzF,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC/B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,kBAAkB,EAAE,MAAM,EAAE,cAAc,GAAG,YAAY,CAQhG"}
1
+ {"version":3,"file":"safety-gate.d.ts","sourceRoot":"","sources":["../src/safety-gate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,eAAO,MAAM,cAAc,gDAAiD,CAAC;AAC7E,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D,MAAM,WAAW,cAAc;IAC7B,yFAAyF;IACzF,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC/B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,kBAAkB,EAAE,MAAM,EAAE,cAAc,GAAG,YAAY,CAQhG"}
@@ -1,11 +1,8 @@
1
1
  /**
2
- * Safety gate — the enforcement point DEC-270 requires: "Any destructive
3
- * action checks the profile's declared level before running, not the
4
- * environment's name or the fact that it's 'not prod.'"
5
- *
6
- * This function does not wire itself into any caller — DEC-270 only asks
7
- * that it exist and be correct; the runners that actually perform
8
- * destructive actions (a different lane's work) call it before acting.
2
+ * DEC-270's enforcement point: destructive actions check the profile's
3
+ * declared safety level, never the environment's name or "not prod."
4
+ * Not wired to any caller here — runners that perform destructive actions
5
+ * (a different lane) call this before acting.
9
6
  */
10
7
  export const GATE_DECISIONS = ["allow", "deny", "requiresApproval"];
11
8
  /**
@@ -17,10 +14,9 @@ export const GATE_DECISIONS = ["allow", "deny", "requiresApproval"];
17
14
  * | `write` | allow | allow | deny |
18
15
  * | `destructiveWithApproval` | allow | allow | requiresApproval |
19
16
  *
20
- * `write` denies destructive rather than escalating it to approval — a
21
- * profile owner who chose `write` explicitly did not choose
22
- * `destructiveWithApproval`, and DEC-270's whole point is that the level
23
- * is declared, not inferred or auto-upgraded.
17
+ * `write` denies destructive rather than escalating to approval — an
18
+ * owner who chose `write` did not choose `destructiveWithApproval`;
19
+ * DEC-270 requires the level be declared, not auto-upgraded.
24
20
  */
25
21
  export function evaluateAction(profile, action) {
26
22
  if (action.destructive) {
@@ -1 +1 @@
1
- {"version":3,"file":"safety-gate.js","sourceRoot":"","sources":["../src/safety-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,kBAAkB,CAAU,CAAC;AAc7E;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,cAAc,CAAC,OAA2B,EAAE,MAAsB;IAChF,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QACvB,OAAO,OAAO,CAAC,WAAW,KAAK,yBAAyB,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,MAAM,CAAC;IACzF,CAAC;IACD,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,OAAO,OAAO,CAAC,WAAW,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IAC/D,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
1
+ {"version":3,"file":"safety-gate.js","sourceRoot":"","sources":["../src/safety-gate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,kBAAkB,CAAU,CAAC;AAc7E;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,cAAc,CAAC,OAA2B,EAAE,MAAsB;IAChF,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QACvB,OAAO,OAAO,CAAC,WAAW,KAAK,yBAAyB,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,MAAM,CAAC;IACzF,CAAC;IACD,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,OAAO,OAAO,CAAC,WAAW,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IAC/D,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
package/package.json CHANGED
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "name": "@descryy/runtime-environment-profile",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "description": "Environment profiles per DEC-270/DEC-266: a user-named target (local booted/attached, remote dev/staging/preview, production) carrying a declared safety level, scoped credentials, and the safety-gate enforcement point destructive actions must call before running.",
6
6
  "license": "UNLICENSED",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/descryhq-wq/descry-runtime.git",
10
+ "directory": "packages/environment-profile"
11
+ },
7
12
  "engines": {
8
13
  "node": ">=22.5"
9
14
  },
@@ -24,6 +29,6 @@
24
29
  "build": "tsc -b"
25
30
  },
26
31
  "dependencies": {
27
- "@descryy/runtime-remote-log-ingestion": "0.2.0"
32
+ "@descryy/runtime-remote-log-ingestion": "0.4.0"
28
33
  }
29
34
  }