Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions config/config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,9 @@ defaults:
# two blocks. `restrict` names what is CLOSED unless granted — an agent listed
# here runs only for an actor whose grants hold agent:run:<name> (or `all`); a
# repo listed here is used only by an actor whose `repos` axis names it (or
# `all`). Everything unlisted is open to everyone who can reach the bot.
# `all`). Unlisted agents and code repos are open to human baselines.
# Credentials and schedules always need explicit agent actions and repo grants;
# `dispatch` alone grants neither.
# Enforcement is at run time against the resolved agent/repo, so it can't be
# bypassed via directives or config scopes. Names must be registered agents /
# owner/name slugs, or the load fails.
Expand All @@ -295,7 +297,9 @@ defaults:
# axes; every axis is a list of names or the explicit word `all`, and an ABSENT
# axis is the empty set (fail-closed). A `slack:` user holds the open chat
# commands and every unrestricted agent whether listed or not — an entry ADDS to
# that; an Access browser session holds every group's read the same way;
# that; an Access browser session holds every group's read and unrestricted
# agents the same way. Both human baselines include open code repos without
# granting repository memory ownership;
# credentials (`http:`, `mcp:`, `access:svc:`) hold exactly their entry and an
# unlisted one holds nothing, not even `dispatch`. A surface entry is unioned
# into every actor of its surface on top of its own entry — a person's entry
Expand Down
34 changes: 34 additions & 0 deletions deploy/cloudflare-memory/runs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1514,6 +1514,40 @@ describe("run history routes", () => {
expect((second.data.items as Array<{ id: string }>).map((row) => row.id)).toEqual(["old-source"]);
});

it("repo complement filters match point and wire semantics in SQLite and reject malformed complements", async () => {
const key = storeKey();
const now = Date.now();
for (const [id, repo] of [
["open", "ACME/OPEN"],
["closed", "Acme/Closed"],
["missing", undefined],
] as const) {
await putDirect(key, record(id, now - 1000, { events: events(1), ...(repo ? { repo } : {}) }));
}
const ids = async (visibleTo: unknown) => {
const res = await post("/runs/list", { storeKey: key, visibleTo });
expect(res.status).toBe(200);
return (res.data.items as Array<{ id: string }>).map((r) => r.id).sort();
};
expect(await ids({ kind: "repos-not-in", repos: ["ACME/CLOSED"] })).toEqual(["open"]);
expect(await ids({ kind: "repos-not-in", repos: [] })).toEqual(["closed", "open"]);
expect(await ids({ kind: "repos-in", repos: ["acme/open"] })).toEqual(["open"]);
expect(
await ids({
kind: "and",
of: [
{ kind: "repos-not-in", repos: ["acme/closed"] },
{ kind: "repos-in", repos: ["ACME/OPEN", "acme/closed"] },
],
}),
).toEqual(["open"]);
for (const repos of ["all", [""], Array.from({ length: 95 }, (_, i) => `acme/r${i}`)]) {
expect((await post("/runs/list", { storeKey: key, visibleTo: { kind: "repos-not-in", repos } })).status).toBe(
400,
);
}
});

it("list with `visibleTo` (authorization.md item 6): channels-in compiles to an indexed IN filter on the ONE LIMITed page query; visibility-in / user-is / or / and follow the same truth table as the in-memory store; none is an empty page; a bad filter is 400, never `all`", async () => {
const key = storeKey();
const now = Date.now();
Expand Down
13 changes: 12 additions & 1 deletion deploy/cloudflare-memory/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7448,7 +7448,17 @@ function visibilitySql(f: RunVisibilityFilter, params: (string | number)[]): str
case "visibility-in":
return inList("channel_visibility", f.visibilities);
case "repos-in":
return inList("repo", f.repos);
return inList(
"LOWER(repo)",
f.repos.map((r) => r.toLowerCase()),
);
case "repos-not-in":
return f.repos.length === 0
? "repo IS NOT NULL"
: `(repo IS NOT NULL AND NOT (${inList(
"LOWER(repo)",
f.repos.map((r) => r.toLowerCase()),
)}))`;
case "user-is":
params.push(f.userId);
return "user_id = ?";
Expand Down Expand Up @@ -7761,6 +7771,7 @@ function boundParameters(f: RunVisibilityFilter): number {
case "visibility-in":
return f.visibilities.length;
case "repos-in":
case "repos-not-in":
return f.repos.length;
case "user-is":
return 1;
Expand Down
12 changes: 12 additions & 0 deletions docs/decisions/0007-authorization-policy-table.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,15 @@ Configuration is one `grants` shape (actor id → actions, channels, repos, with
## Pattern

A rules table for authorization, evaluated by one function. Strategy at the identity-resolution seam per surface. Fail-closed by construction.

## Amended 2026-10-05

**Re-evaluation:** compile human code access without memory ownership.

The original table separated identity from authority, but ConfigStore's open-unless-restricted shortcuts admitted ungranted agents and repos for machine credentials. The correction compiles openness into human namespace baselines and makes the two dispatch gates ask the existing policy table. A credential, including one bound to a person, holds only its configured grants; recursive delegation intersects authority on every axis.

Code access needs a distinct compiled repo axis: the existing explicit `repos` axis also authorizes repository memory through `owner-of`. Giving it human openness would expose memory that was previously denied. Optional `repoAccess` inherits explicit `repos` for older/manual actors; human baselines add a finite complement of restricted repos. Configuration remains the same three literal axes. Union and intersection preserve code access and ownership independently and normalize repository case.

The table adds the `repo-access` condition for `repo:use`; `owner-of` retains explicit ownership for repo memory and repo execution. The condition compiles to `repos-in` or bounded `repos-not-in`. A complement never matches a missing repo. Reference, wire and SQLite evaluators share those semantics; unknown filter kinds are rejected. Existing run-read rows do not emit complements, so their stored filters retain their prior shape.

Rejected alternatives: widening ownership would change memory access; a user-kind or root-principal shortcut would exceed a bound credential or mixed delegation ceiling; closing global production resources would move credential safety into deployment configuration. The cost is finite-complement arithmetic in the existing model and one bounded store predicate, rather than a second policy mechanism. Regression proofs cover parsed credential scope, human defaults, bound admins, relays, nested delegation, memory ownership and SQLite parity.
14 changes: 8 additions & 6 deletions docs/public/screenshots/manifest/conductor-page.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,21 @@
"src/channels/webAssets.ts": "402b7a1bdb2c1967960dfc06673dca3e6dbaeb25718d1bdcb1f0c59302d19471",
"src/channels/webSeed.ts": "c6db9be137ba5aa4f54687679d93762f967af4064e0211e01e7ba981db033199",
"src/channels/webShell.ts": "c3f74aeb04010dd88204b86e7a782f5a45f6731bf6f0f29cc8a4b5039df8f090",
"src/config.ts": "40bba78c3137378eaddea54388ca3ccedf0090921d24144301b00daf9441f9af",
"src/config.ts": "128127cfff41596b15116aa941cbe4762ae0b6e5242a6b58ebf1e43aea40e2f2",
"src/config/agents.ts": "ba7184ebcb6be04515a28e44246a471c5afc75b24bd212e9b4213ef46855c389",
"src/config/profile.ts": "92e7edb8dfb5e4f6f255a00473c0aa436832b0797d54562b17ff96e3da49361c",
"src/config/validate.ts": "3308b7fe8a6c0cb275532b238bdac4236b663dc5dd4d9e0cb2f0caf3ddd6e874",
"src/configDocument.ts": "7a963c8b60516572353e02112a477568c266d75dd64f0fde15c8a476891c03b5",
"src/core/answerOutcome.ts": "f52425f8c6cf107d358918faed8065a8f19d46350e0cfe77f581fb85b5a19520",
"src/core/anthropicApiKey.ts": "d7dbb29f657da921981a4356527423c8640f8492d1890c097aa98eefa33f2f40",
"src/core/audienceDecision.ts": "df56c7a8b56022eee0b185c463601ce276cce0a04dceff15e0832f6f13cc764a",
"src/core/authz/authorize.ts": "652bfe5132b3103a55b9d1490cbbcd638a338d4ac08e15445afe49040982f4a7",
"src/core/authz/grants.ts": "39927003f38a10d84b15b0656325dc337cb64c973ca20b043f1dd98870e509df",
"src/core/authz/policy.ts": "d36e4b92857fa3f5ff9e20b66cfbf78f112c0c5e628b3f28eb8d634aa4420736",
"src/core/authz/actor.ts": "1fb77d4cf0781b6a027dcebf8b9bd2c0ef94d03887fd6449c5947fc26304b89d",
"src/core/authz/authorize.ts": "3765d297702d46eb9a056652705b107cca31ec8b7447d02bc49fa275618ac220",
"src/core/authz/grantSets.ts": "92ddc2c565c69aecc96f5995f43b5805f74e6391435888fc580ab584e4e99b52",
"src/core/authz/grants.ts": "61f7fe1dca21045ce1e2b18adf5acdc02c98262dee114945f201e259e8009b7a",
"src/core/authz/policy.ts": "4c9e010002a779146d40be7319af73029171618cb2525f67dea9d61f3f560488",
"src/core/authz/resource.ts": "8bf899b1c2f8e8db5650042b2af0f0a2359871c12685fd7dc7eaec6e1769d931",
"src/core/authz/types.ts": "58212afa0722cf062de5c2ef26fd356e9c3c707134e0718f0676159e680b8151",
"src/core/authz/types.ts": "5e02983014ffad24c0d0c4a7fe15d397d39c7138bc4f6f8528334622af4d558e",
"src/core/authz/viewAs.ts": "4b162694a1a2c880df7f436027991d252f37318a44d8a5175685a3d81b0e63ec",
"src/core/branchIdentityBaseline.ts": "b13b930408b3d87887736275b08ad0b7f740514082ca3b2b3f9a75c2f8ad5ead",
"src/core/branchPublication.ts": "c4d749996d997e598993a46aadac6f386071e106bad1c52275ddddf03afec6a2",
Expand Down Expand Up @@ -156,7 +158,7 @@
"src/core/runMetrics.ts": "3fcf56c30ffac9dedf68e901fba21fd12c021b94c5e79512905c6bcde73ec2ff",
"src/core/runOwner.ts": "6e8f655ed2c34d08e3c258fc661309804e77d5d1b0972b5f12867427502b37cd",
"src/core/runPace.ts": "5bec2b2a007c7db8461b6414fe18d94775bf93763336268c60fcc83177874e97",
"src/core/runRecord.ts": "a03ab7cba2695df08cb862d38f5b0fa2ddc932f4e29a35ca4f23eba901947a14",
"src/core/runRecord.ts": "633c0c829ebff85da0fffaabf9abb1f478c250668face2a603185659d6e9f689",
"src/core/runShape.ts": "0962881b1f2201c02214b8ee6d897f279dc90f64feb9a57c3950b23e7ea8d1f2",
"src/core/runStore.ts": "fcb9bcd6d37da0a6dad638b519c005afea665f6e755730aa22636a974e0547d2",
"src/core/runStoreConstants.ts": "c9b1b1cc9b3d8532fce25f41bea2c49000d5a4debd6671d6f5139a8cba49632e",
Expand Down
14 changes: 8 additions & 6 deletions docs/public/screenshots/manifest/costs-models.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,21 @@
"src/channels/webAssets.ts": "402b7a1bdb2c1967960dfc06673dca3e6dbaeb25718d1bdcb1f0c59302d19471",
"src/channels/webSeed.ts": "c6db9be137ba5aa4f54687679d93762f967af4064e0211e01e7ba981db033199",
"src/channels/webShell.ts": "c3f74aeb04010dd88204b86e7a782f5a45f6731bf6f0f29cc8a4b5039df8f090",
"src/config.ts": "40bba78c3137378eaddea54388ca3ccedf0090921d24144301b00daf9441f9af",
"src/config.ts": "128127cfff41596b15116aa941cbe4762ae0b6e5242a6b58ebf1e43aea40e2f2",
"src/config/agents.ts": "ba7184ebcb6be04515a28e44246a471c5afc75b24bd212e9b4213ef46855c389",
"src/config/profile.ts": "92e7edb8dfb5e4f6f255a00473c0aa436832b0797d54562b17ff96e3da49361c",
"src/config/validate.ts": "3308b7fe8a6c0cb275532b238bdac4236b663dc5dd4d9e0cb2f0caf3ddd6e874",
"src/configDocument.ts": "7a963c8b60516572353e02112a477568c266d75dd64f0fde15c8a476891c03b5",
"src/core/answerOutcome.ts": "f52425f8c6cf107d358918faed8065a8f19d46350e0cfe77f581fb85b5a19520",
"src/core/anthropicApiKey.ts": "d7dbb29f657da921981a4356527423c8640f8492d1890c097aa98eefa33f2f40",
"src/core/audienceDecision.ts": "df56c7a8b56022eee0b185c463601ce276cce0a04dceff15e0832f6f13cc764a",
"src/core/authz/authorize.ts": "652bfe5132b3103a55b9d1490cbbcd638a338d4ac08e15445afe49040982f4a7",
"src/core/authz/grants.ts": "39927003f38a10d84b15b0656325dc337cb64c973ca20b043f1dd98870e509df",
"src/core/authz/policy.ts": "d36e4b92857fa3f5ff9e20b66cfbf78f112c0c5e628b3f28eb8d634aa4420736",
"src/core/authz/actor.ts": "1fb77d4cf0781b6a027dcebf8b9bd2c0ef94d03887fd6449c5947fc26304b89d",
"src/core/authz/authorize.ts": "3765d297702d46eb9a056652705b107cca31ec8b7447d02bc49fa275618ac220",
"src/core/authz/grantSets.ts": "92ddc2c565c69aecc96f5995f43b5805f74e6391435888fc580ab584e4e99b52",
"src/core/authz/grants.ts": "61f7fe1dca21045ce1e2b18adf5acdc02c98262dee114945f201e259e8009b7a",
"src/core/authz/policy.ts": "4c9e010002a779146d40be7319af73029171618cb2525f67dea9d61f3f560488",
"src/core/authz/resource.ts": "8bf899b1c2f8e8db5650042b2af0f0a2359871c12685fd7dc7eaec6e1769d931",
"src/core/authz/types.ts": "58212afa0722cf062de5c2ef26fd356e9c3c707134e0718f0676159e680b8151",
"src/core/authz/types.ts": "5e02983014ffad24c0d0c4a7fe15d397d39c7138bc4f6f8528334622af4d558e",
"src/core/authz/viewAs.ts": "4b162694a1a2c880df7f436027991d252f37318a44d8a5175685a3d81b0e63ec",
"src/core/branchIdentityBaseline.ts": "b13b930408b3d87887736275b08ad0b7f740514082ca3b2b3f9a75c2f8ad5ead",
"src/core/branchPublication.ts": "c4d749996d997e598993a46aadac6f386071e106bad1c52275ddddf03afec6a2",
Expand Down Expand Up @@ -148,7 +150,7 @@
"src/core/runLedger/inboxMessage.ts": "daac9f5ec1ae5a180d20cb589b5c0154c913e8c4b7b597f7907a7ffc2c0ba6ba",
"src/core/runMetrics.ts": "3fcf56c30ffac9dedf68e901fba21fd12c021b94c5e79512905c6bcde73ec2ff",
"src/core/runPace.ts": "5bec2b2a007c7db8461b6414fe18d94775bf93763336268c60fcc83177874e97",
"src/core/runRecord.ts": "a03ab7cba2695df08cb862d38f5b0fa2ddc932f4e29a35ca4f23eba901947a14",
"src/core/runRecord.ts": "633c0c829ebff85da0fffaabf9abb1f478c250668face2a603185659d6e9f689",
"src/core/runShape.ts": "0962881b1f2201c02214b8ee6d897f279dc90f64feb9a57c3950b23e7ea8d1f2",
"src/core/runStore.ts": "fcb9bcd6d37da0a6dad638b519c005afea665f6e755730aa22636a974e0547d2",
"src/core/runStoreConstants.ts": "c9b1b1cc9b3d8532fce25f41bea2c49000d5a4debd6671d6f5139a8cba49632e",
Expand Down
14 changes: 8 additions & 6 deletions docs/public/screenshots/manifest/costs-users.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,21 @@
"src/channels/webAssets.ts": "402b7a1bdb2c1967960dfc06673dca3e6dbaeb25718d1bdcb1f0c59302d19471",
"src/channels/webSeed.ts": "c6db9be137ba5aa4f54687679d93762f967af4064e0211e01e7ba981db033199",
"src/channels/webShell.ts": "c3f74aeb04010dd88204b86e7a782f5a45f6731bf6f0f29cc8a4b5039df8f090",
"src/config.ts": "40bba78c3137378eaddea54388ca3ccedf0090921d24144301b00daf9441f9af",
"src/config.ts": "128127cfff41596b15116aa941cbe4762ae0b6e5242a6b58ebf1e43aea40e2f2",
"src/config/agents.ts": "ba7184ebcb6be04515a28e44246a471c5afc75b24bd212e9b4213ef46855c389",
"src/config/profile.ts": "92e7edb8dfb5e4f6f255a00473c0aa436832b0797d54562b17ff96e3da49361c",
"src/config/validate.ts": "3308b7fe8a6c0cb275532b238bdac4236b663dc5dd4d9e0cb2f0caf3ddd6e874",
"src/configDocument.ts": "7a963c8b60516572353e02112a477568c266d75dd64f0fde15c8a476891c03b5",
"src/core/answerOutcome.ts": "f52425f8c6cf107d358918faed8065a8f19d46350e0cfe77f581fb85b5a19520",
"src/core/anthropicApiKey.ts": "d7dbb29f657da921981a4356527423c8640f8492d1890c097aa98eefa33f2f40",
"src/core/audienceDecision.ts": "df56c7a8b56022eee0b185c463601ce276cce0a04dceff15e0832f6f13cc764a",
"src/core/authz/authorize.ts": "652bfe5132b3103a55b9d1490cbbcd638a338d4ac08e15445afe49040982f4a7",
"src/core/authz/grants.ts": "39927003f38a10d84b15b0656325dc337cb64c973ca20b043f1dd98870e509df",
"src/core/authz/policy.ts": "d36e4b92857fa3f5ff9e20b66cfbf78f112c0c5e628b3f28eb8d634aa4420736",
"src/core/authz/actor.ts": "1fb77d4cf0781b6a027dcebf8b9bd2c0ef94d03887fd6449c5947fc26304b89d",
"src/core/authz/authorize.ts": "3765d297702d46eb9a056652705b107cca31ec8b7447d02bc49fa275618ac220",
"src/core/authz/grantSets.ts": "92ddc2c565c69aecc96f5995f43b5805f74e6391435888fc580ab584e4e99b52",
"src/core/authz/grants.ts": "61f7fe1dca21045ce1e2b18adf5acdc02c98262dee114945f201e259e8009b7a",
"src/core/authz/policy.ts": "4c9e010002a779146d40be7319af73029171618cb2525f67dea9d61f3f560488",
"src/core/authz/resource.ts": "8bf899b1c2f8e8db5650042b2af0f0a2359871c12685fd7dc7eaec6e1769d931",
"src/core/authz/types.ts": "58212afa0722cf062de5c2ef26fd356e9c3c707134e0718f0676159e680b8151",
"src/core/authz/types.ts": "5e02983014ffad24c0d0c4a7fe15d397d39c7138bc4f6f8528334622af4d558e",
"src/core/authz/viewAs.ts": "4b162694a1a2c880df7f436027991d252f37318a44d8a5175685a3d81b0e63ec",
"src/core/branchIdentityBaseline.ts": "b13b930408b3d87887736275b08ad0b7f740514082ca3b2b3f9a75c2f8ad5ead",
"src/core/branchPublication.ts": "c4d749996d997e598993a46aadac6f386071e106bad1c52275ddddf03afec6a2",
Expand Down Expand Up @@ -148,7 +150,7 @@
"src/core/runLedger/inboxMessage.ts": "daac9f5ec1ae5a180d20cb589b5c0154c913e8c4b7b597f7907a7ffc2c0ba6ba",
"src/core/runMetrics.ts": "3fcf56c30ffac9dedf68e901fba21fd12c021b94c5e79512905c6bcde73ec2ff",
"src/core/runPace.ts": "5bec2b2a007c7db8461b6414fe18d94775bf93763336268c60fcc83177874e97",
"src/core/runRecord.ts": "a03ab7cba2695df08cb862d38f5b0fa2ddc932f4e29a35ca4f23eba901947a14",
"src/core/runRecord.ts": "633c0c829ebff85da0fffaabf9abb1f478c250668face2a603185659d6e9f689",
"src/core/runShape.ts": "0962881b1f2201c02214b8ee6d897f279dc90f64feb9a57c3950b23e7ea8d1f2",
"src/core/runStore.ts": "fcb9bcd6d37da0a6dad638b519c005afea665f6e755730aa22636a974e0547d2",
"src/core/runStoreConstants.ts": "c9b1b1cc9b3d8532fce25f41bea2c49000d5a4debd6671d6f5139a8cba49632e",
Expand Down
Loading