@melra/policy-core 0.3.0-alpha.10 → 0.3.0-alpha.11

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/README.md CHANGED
@@ -30,12 +30,22 @@ never reaches a runtime.
30
30
  - Terminal commands must be allowlisted by basename. Shell interpreters and
31
31
  `sudo`/`su` are denied unconditionally. `git` counts as a read only for a small
32
32
  read-only subcommand set; `npm`, `npx`, and `pnpm` are high-risk mutations.
33
- - Browser destinations default to `allowedDomains: ["*"]` with localhost allowed,
34
- because the allowlist is a narrowing control, not the safety boundary — the
35
- browser runtime independently blocks private and metadata destinations.
33
+ - Browser and HTTP destinations default to `allowedDomains: ["*"]` with localhost
34
+ allowed, because the allowlist is a narrowing control, not the safety boundary
35
+ — `assertSafeDestination`, exported from this package, is what blocks private
36
+ and metadata destinations, and it holds even with `allowedDomains: ["*"]`.
36
37
  - `classifyOperation` is the single place effect and risk are decided. An action
37
38
  that is not classified there is mis-classified, not unclassified.
38
39
 
40
+ ## The destination boundary
41
+
42
+ `assertSafeDestination` lives here rather than in a runtime so every adapter that
43
+ opens a socket shares one implementation. It rejects non-`http(s)` protocols and
44
+ URL credentials, resolves DNS first and checks **every** answer, and blocks
45
+ private, loopback, link-local, and cloud-metadata ranges in IPv4, IPv6, and
46
+ IPv4-mapped IPv6 form. It returns the address it checked so the caller can pin
47
+ the socket to it instead of letting the name resolve a second time.
48
+
39
49
  ## Capability grants
40
50
 
41
51
  `policy.capabilities` is authority; everything above is rules. It is empty by
@@ -72,6 +82,12 @@ rules it was not written for.
72
82
  caller's own `forbiddenEffects` and `constraints` are honoured, because those are
73
83
  the caller bounding its own task rather than a guardrail MELRA imposes.
74
84
 
85
+ `mode` is `developer` or `enforced` and is not about a request at all: it records
86
+ how this kernel is deployed, and therefore what it refuses about its own setup
87
+ rather than about a caller. `deploymentMode` parses it from the flag, the
88
+ environment, and the policy file through one function, so a typo fails loudly
89
+ instead of leaving a machine permissive with a config that claims otherwise.
90
+
75
91
  Requires Node.js 22 or newer. Full documentation:
76
92
  [github.com/XAGI-Lab/melra](https://github.com/XAGI-Lab/melra)
77
93
 
@@ -0,0 +1,29 @@
1
+ import type { CredentialDefinition } from "@melra/protocol";
2
+ export interface BrokeredHeaders {
3
+ /** Headers to merge into the outgoing request, lowercased. */
4
+ headers: Record<string, string>;
5
+ /** Names of the credentials that were injected — never their values. */
6
+ used: string[];
7
+ }
8
+ /**
9
+ * Holds the secrets so the agent does not have to.
10
+ *
11
+ * An adapter asks for the headers one operation may carry and gets back either
12
+ * a header it can send or nothing at all. It never receives the secret as a
13
+ * value it could log, return, or persist, and the broker never writes one down:
14
+ * every call re-reads the source, so rotating a key takes effect on the next
15
+ * request rather than on the next restart.
16
+ */
17
+ export declare class CredentialBroker {
18
+ private readonly definitions;
19
+ private readonly environment;
20
+ constructor(definitions?: Record<string, CredentialDefinition>, environment?: NodeJS.ProcessEnv);
21
+ /** True when nothing is configured, so a caller can skip the await. */
22
+ get empty(): boolean;
23
+ headersFor(request: {
24
+ host: string;
25
+ capability: string;
26
+ target: string;
27
+ }): Promise<BrokeredHeaders>;
28
+ }
29
+ //# sourceMappingURL=credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAE5D,MAAM,WAAW,eAAe;IAC9B,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,wEAAwE;IACxE,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AA4DD;;;;;;;;GAQG;AACH,qBAAa,gBAAgB;IAEzB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,WAAW;gBADX,WAAW,GAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAM,EACtD,WAAW,GAAE,MAAM,CAAC,UAAwB;IAG/D,uEAAuE;IACvE,IAAI,KAAK,IAAI,OAAO,CAEnB;IAEK,UAAU,CAAC,OAAO,EAAE;QACxB,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;KAChB,GAAG,OAAO,CAAC,eAAe,CAAC;CAkC7B"}
@@ -0,0 +1,112 @@
1
+ // Copyright 2026 XAGI Labs Private Limited
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { constants } from "node:fs";
4
+ import { lstat, open } from "node:fs/promises";
5
+ function patternMatches(pattern, value) {
6
+ if (pattern === "*")
7
+ return true;
8
+ const escaped = pattern
9
+ .replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
10
+ .replaceAll("\\*", ".*");
11
+ return new RegExp(`^${escaped}$`).test(value);
12
+ }
13
+ function errorCode(error) {
14
+ return error !== null && typeof error === "object" && "code" in error
15
+ ? String(error.code)
16
+ : undefined;
17
+ }
18
+ /**
19
+ * Read a secret out of a file the same way the payload key is read: refuse a
20
+ * symlink, refuse anything but a regular file, and refuse a mode any other user
21
+ * can read. A secret in a world-readable file is one the agent could have read
22
+ * for itself, which would make the whole broker decorative.
23
+ */
24
+ async function readSecretFile(path) {
25
+ const before = await lstat(path);
26
+ if (before.isSymbolicLink()) {
27
+ throw new Error("credential_file_must_not_be_symlink");
28
+ }
29
+ let handle;
30
+ try {
31
+ handle = await open(path, constants.O_RDONLY |
32
+ (process.platform === "win32" ? 0 : constants.O_NOFOLLOW));
33
+ }
34
+ catch (error) {
35
+ if (errorCode(error) === "ELOOP") {
36
+ throw new Error("credential_file_must_not_be_symlink");
37
+ }
38
+ throw error;
39
+ }
40
+ try {
41
+ const metadata = await handle.stat();
42
+ if (!metadata.isFile() ||
43
+ metadata.dev !== before.dev ||
44
+ metadata.ino !== before.ino) {
45
+ throw new Error("credential_file_not_regular_file");
46
+ }
47
+ if (process.platform !== "win32" && (metadata.mode & 0o077) !== 0) {
48
+ throw new Error("credential_file_permissions_too_open");
49
+ }
50
+ const secret = (await handle.readFile("utf8")).trim();
51
+ if (secret === "")
52
+ throw new Error("credential_file_empty");
53
+ return secret;
54
+ }
55
+ finally {
56
+ await handle.close();
57
+ }
58
+ }
59
+ /**
60
+ * Holds the secrets so the agent does not have to.
61
+ *
62
+ * An adapter asks for the headers one operation may carry and gets back either
63
+ * a header it can send or nothing at all. It never receives the secret as a
64
+ * value it could log, return, or persist, and the broker never writes one down:
65
+ * every call re-reads the source, so rotating a key takes effect on the next
66
+ * request rather than on the next restart.
67
+ */
68
+ export class CredentialBroker {
69
+ definitions;
70
+ environment;
71
+ constructor(definitions = {}, environment = process.env) {
72
+ this.definitions = definitions;
73
+ this.environment = environment;
74
+ }
75
+ /** True when nothing is configured, so a caller can skip the await. */
76
+ get empty() {
77
+ return Object.keys(this.definitions).length === 0;
78
+ }
79
+ async headersFor(request) {
80
+ const headers = {};
81
+ const used = [];
82
+ const operation = `${request.capability}:${request.target}`;
83
+ const host = request.host.toLowerCase();
84
+ for (const [name, definition] of Object.entries(this.definitions)) {
85
+ if (!definition.hosts.some((pattern) => patternMatches(pattern.toLowerCase(), host))) {
86
+ // Not this credential's host. The request still goes out, just
87
+ // unauthenticated — which is what stops a URL the caller chose from
88
+ // walking somebody's bearer token to an origin it was never issued for.
89
+ continue;
90
+ }
91
+ if (!patternMatches(definition.capability, operation)) {
92
+ // Refused before the source is touched. The credential could perform
93
+ // this call; the delegation did not say it may, and that gap is the
94
+ // entire difference between holding a credential and holding authority.
95
+ throw new Error(`credential_capability_not_covered:${name}`);
96
+ }
97
+ const secret = "env" in definition.source
98
+ ? this.environment[definition.source.env]
99
+ : await readSecretFile(definition.source.file);
100
+ if (secret === undefined || secret === "") {
101
+ throw new Error(`credential_source_missing:${name}`);
102
+ }
103
+ headers[definition.inject.header.toLowerCase()] =
104
+ definition.inject.scheme === undefined
105
+ ? secret
106
+ : `${definition.inject.scheme} ${secret}`;
107
+ used.push(name);
108
+ }
109
+ return { headers, used };
110
+ }
111
+ }
112
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=credentials.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.test.d.ts","sourceRoot":"","sources":["../src/credentials.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,103 @@
1
+ // Copyright 2026 XAGI Labs Private Limited
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { chmod, mkdtemp, rm, writeFile } from "node:fs/promises";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { afterAll, describe, expect, it } from "vitest";
7
+ import { CredentialsSchema } from "@melra/protocol";
8
+ import { CredentialBroker } from "./credentials.js";
9
+ const roots = [];
10
+ afterAll(async () => {
11
+ for (const root of roots)
12
+ await rm(root, { recursive: true, force: true });
13
+ });
14
+ async function secretFile(mode) {
15
+ const root = await mkdtemp(join(tmpdir(), "melra-credentials-"));
16
+ roots.push(root);
17
+ const path = join(root, "stripe.key");
18
+ await writeFile(path, "sk_live_from_a_file\n");
19
+ await chmod(path, mode);
20
+ return path;
21
+ }
22
+ const stripe = {
23
+ stripe: {
24
+ source: { env: "MELRA_TEST_STRIPE_KEY" },
25
+ inject: { header: "Authorization", scheme: "Bearer" },
26
+ hosts: ["api.stripe.com"],
27
+ capability: "http.post:https://api.stripe.com/v1/refunds*",
28
+ },
29
+ };
30
+ const broker = (definitions = stripe, environment = { MELRA_TEST_STRIPE_KEY: "sk_live_secret" }) => new CredentialBroker(CredentialsSchema.parse(definitions), environment);
31
+ describe("credential broker", () => {
32
+ it("injects the secret for an operation the delegation covers", async () => {
33
+ const { headers, used } = await broker().headersFor({
34
+ host: "api.stripe.com",
35
+ capability: "http.post",
36
+ target: "https://api.stripe.com/v1/refunds",
37
+ });
38
+ expect(headers.authorization).toBe("Bearer sk_live_secret");
39
+ // The names are what a caller may see. The values are what it may not.
40
+ expect(used).toEqual(["stripe"]);
41
+ });
42
+ it("sends a request to another host unauthenticated rather than refusing it", async () => {
43
+ const { headers, used } = await broker().headersFor({
44
+ host: "api.example.com",
45
+ capability: "http.post",
46
+ target: "https://api.example.com/v1/refunds",
47
+ });
48
+ expect(headers).toEqual({});
49
+ expect(used).toEqual([]);
50
+ });
51
+ it("refuses an operation outside the delegation without reading the secret", async () => {
52
+ // The source points at a file that does not exist. A `credential_source_*`
53
+ // or `ENOENT` here would mean the broker read first and checked second —
54
+ // the refusal has to land before the plaintext is anywhere near the process.
55
+ const refusing = broker({
56
+ stripe: {
57
+ ...stripe.stripe,
58
+ source: { file: join(tmpdir(), "melra-nonexistent-credential") },
59
+ },
60
+ });
61
+ await expect(refusing.headersFor({
62
+ host: "api.stripe.com",
63
+ capability: "http.delete",
64
+ target: "https://api.stripe.com/v1/customers/cus_1",
65
+ })).rejects.toThrow("credential_capability_not_covered:stripe");
66
+ });
67
+ it("reads a mode-0600 file and refuses a permissive one", async () => {
68
+ const path = await secretFile(0o600);
69
+ const request = {
70
+ host: "api.stripe.com",
71
+ capability: "http.post",
72
+ target: "https://api.stripe.com/v1/refunds",
73
+ };
74
+ const definitions = {
75
+ stripe: { ...stripe.stripe, source: { file: path } },
76
+ };
77
+ const { headers } = await broker(definitions).headersFor(request);
78
+ expect(headers.authorization).toBe("Bearer sk_live_from_a_file");
79
+ await chmod(path, 0o644);
80
+ const permissive = broker(definitions).headersFor(request);
81
+ if (process.platform === "win32") {
82
+ // Windows does not carry the mode bits this checks, so the guard cannot
83
+ // fire there. Asserting it would only test the platform.
84
+ await expect(permissive).resolves.toBeDefined();
85
+ }
86
+ else {
87
+ await expect(permissive).rejects.toThrow("credential_file_permissions_too_open");
88
+ }
89
+ });
90
+ it("fails loudly when the source names nothing", async () => {
91
+ await expect(broker(stripe, {}).headersFor({
92
+ host: "api.stripe.com",
93
+ capability: "http.post",
94
+ target: "https://api.stripe.com/v1/refunds",
95
+ })).rejects.toThrow("credential_source_missing:stripe");
96
+ });
97
+ it("rejects an unknown field in a definition", () => {
98
+ expect(() => CredentialsSchema.parse({
99
+ stripe: { ...stripe.stripe, allowRedirects: true },
100
+ })).toThrow();
101
+ });
102
+ });
103
+ //# sourceMappingURL=credentials.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.test.js","sourceRoot":"","sources":["../src/credentials.test.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ApprovalChallenge, ApprovalResponse, CapabilityGrant, CapabilityTrait, Effect, EvidencePredicate, Identity, Operation, PolicyDecision, Risk, TaskRequest } from "@melra/protocol";
1
+ import type { ApprovalChallenge, ApprovalResponse, CapabilityGrant, CapabilityTrait, CredentialDefinition, DeploymentMode, Effect, EvidencePredicate, HttpOperation, Identity, Operation, PolicyDecision, Risk, TaskRequest } from "@melra/protocol";
2
2
  export interface LocalPolicy {
3
3
  version: string;
4
4
  workspaceRoot: string;
@@ -53,6 +53,16 @@ export interface LocalPolicy {
53
53
  * holding authority over one directory rather than over the whole workspace.
54
54
  */
55
55
  capabilities: CapabilityGrant[];
56
+ /**
57
+ * Secrets the kernel injects on the caller's behalf, keyed by a name the
58
+ * caller may see in a receipt. Empty by default.
59
+ *
60
+ * The agent never holds one. It names an operation; if a definition's `hosts`
61
+ * cover the destination *and* its `capability` covers the operation, the
62
+ * kernel reads the secret at send time and puts it on the wire. See
63
+ * `CredentialBroker` for why those two lists behave differently on a miss.
64
+ */
65
+ credentials: Record<string, CredentialDefinition>;
56
66
  /**
57
67
  * Stops a task from re-running an operation whose target keeps failing.
58
68
  *
@@ -79,10 +89,32 @@ export interface LocalPolicy {
79
89
  * derives it from another setting.
80
90
  */
81
91
  unhinged: boolean;
92
+ /**
93
+ * How this kernel is deployed, and therefore what it refuses about its own
94
+ * setup rather than about a caller's request.
95
+ *
96
+ * `developer` is the default and changes nothing: MELRA governs the effects it
97
+ * is asked for, and the operator accepts that the harness may have another way
98
+ * to the same disk. `enforced` is the operator asserting there is no other way
99
+ * — the harness is sandboxed, holds no privileged secrets, and reaches systems
100
+ * only through here. MELRA cannot verify that assertion, so what it does
101
+ * instead is remove every door it owns that would contradict it: no
102
+ * `--unsafe-local`, no bind outside loopback, no client registering itself.
103
+ *
104
+ * Named on every receipt, because "was this effect governed by the only door
105
+ * or by one of several" is not something an auditor should have to infer.
106
+ */
107
+ mode: DeploymentMode;
82
108
  }
83
109
  export interface PolicyEvaluation {
84
110
  decision: PolicyDecision;
85
111
  challenge?: ApprovalChallenge;
112
+ /**
113
+ * The grant that authorised this, when `policy.capabilities` is non-empty.
114
+ * What the commit meters against — absent means no grants are configured and
115
+ * there is nothing to draw down.
116
+ */
117
+ grantId?: string;
86
118
  }
87
119
  export declare function normalizeCommandName(command: string): string;
88
120
  /**
@@ -103,6 +135,7 @@ export declare function normalizeCommandName(command: string): string;
103
135
  * network at all, not "no network except the browser".
104
136
  */
105
137
  export type { CapabilityTrait } from "@melra/protocol";
138
+ export { CredentialBroker, type BrokeredHeaders, } from "./credentials.js";
106
139
  /**
107
140
  * Classify a terminal command by what it does, not just by its name.
108
141
  *
@@ -122,6 +155,8 @@ export declare function classifyOperation(operation: Operation): {
122
155
  capability: string;
123
156
  target: string;
124
157
  traits: CapabilityTrait[];
158
+ /** What the caller declared this operation moves, when it declared one. */
159
+ spend?: DeclaredSpend;
125
160
  };
126
161
  /**
127
162
  * Evidence a mutation must satisfy when the caller declared none.
@@ -140,16 +175,51 @@ export declare function classifyOperation(operation: Operation): {
140
175
  */
141
176
  export declare function defaultEvidenceFor(operation: Operation): EvidencePredicate[];
142
177
  /**
143
- * Why the issued capabilities do not cover this effect, or `undefined` if they
144
- * do. An empty grant list is not a closed world — it means the operator has not
145
- * issued grants at all, and the rest of policy decides on its own.
178
+ * What a grant has already drawn down, read from wherever commits are counted.
179
+ *
180
+ * A lookup rather than a number because policy is evaluated more than once per
181
+ * task and a grant may be metered by any of several bounds; the caller that
182
+ * owns the durable count decides how to answer.
146
183
  */
147
- export declare function capabilityRefusal(classified: {
184
+ /**
185
+ * What an operation declared it moves. Taken from the schema that defines it
186
+ * rather than restated, so a field added there cannot go unnoticed here.
187
+ */
188
+ export type DeclaredSpend = NonNullable<HttpOperation["spend"]>;
189
+ export type CapabilityUsageReader = (grantId: string) => {
190
+ /** Operations committed against this grant, ever. */
191
+ operations: number;
192
+ /** Declared amounts committed against it in the last 24 hours. */
193
+ amountToday: number;
194
+ };
195
+ /** What the issued grants say about one effect. */
196
+ export interface CapabilityCheck {
197
+ /** Why they do not cover it, or `undefined` if they do. */
198
+ refusal?: string;
199
+ /** The grant it will be metered against, when a grant was needed at all. */
200
+ grantId?: string;
201
+ }
202
+ type ClassifiedForGrant = {
148
203
  effect: Effect;
149
204
  capability: string;
150
205
  target: string;
151
- }, identity: Identity, policy: LocalPolicy): string | undefined;
152
- export declare function evaluatePolicy(taskId: string, request: TaskRequest, policy: LocalPolicy): PolicyEvaluation;
206
+ spend?: DeclaredSpend;
207
+ };
208
+ /** Milliseconds in the rolling window `provider.dailyMax` is measured over. */
209
+ export declare const CAPABILITY_DAY_MS: number;
210
+ /**
211
+ * Which issued grant covers this effect, or why none does.
212
+ *
213
+ * An empty grant list is not a closed world — it means the operator has not
214
+ * issued grants at all, and the rest of policy decides on its own.
215
+ */
216
+ export declare function capabilityCheck(classified: ClassifiedForGrant, identity: Identity, policy: LocalPolicy, usage?: CapabilityUsageReader): CapabilityCheck;
217
+ /**
218
+ * Why the issued capabilities do not cover this effect, or `undefined` if they
219
+ * do. `capabilityCheck` also reports which grant covered it.
220
+ */
221
+ export declare function capabilityRefusal(classified: ClassifiedForGrant, identity: Identity, policy: LocalPolicy, usage?: CapabilityUsageReader): string | undefined;
222
+ export declare function evaluatePolicy(taskId: string, request: TaskRequest, policy: LocalPolicy, usage?: CapabilityUsageReader): PolicyEvaluation;
153
223
  export declare function digestOperation(taskId: string, operation: Operation): string;
154
224
  export declare function validateApproval(challenge: ApprovalChallenge | undefined, response: ApprovalResponse | undefined): {
155
225
  ok: true;
@@ -157,4 +227,5 @@ export declare function validateApproval(challenge: ApprovalChallenge | undefine
157
227
  ok: false;
158
228
  reason: string;
159
229
  };
230
+ export * from "./network-policy.js";
160
231
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}