Commit 6434774
authored
feat(agents-server): add permission enforcement (#4475)
## Motivation
Agents Server needs authorization at every entity access path so tenants
can limit which principals can spawn types and access individual entity
instances. The default model should keep entity instances owner-private
while still allowing common sharing flows through explicit grants,
principal-kind grants, spawn-time inheritance, and Electric-visible
effective permissions.
fixes #4464
## What changed
- Added permission storage for entity type spawn grants, entity instance
grants, effective permissions, lineage, and shared-state links.
- Added owner-default permission checks plus middleware enforcement for
entity routes, spawn, durable streams, attachments, schedules,
event-source subscriptions, tags, inbox edits, and shared-state streams.
- Made new entity type registration available to any authenticated
principal by default. Runtime entity definitions can publish their
default spawn grants during `registerTypes()`.
- Added default built-in `spawn` grants for `principal_kind=user` on
Horton and Worker.
- Materialized descendant and copy-at-spawn entity grants into
`entity_effective_permissions` for point decisions and Electric
visibility.
- Scoped entity list, entity type list, Electric shape predicates, and
`entities(tags)` observation streams by the authenticated principal.
Entity `manage` grants are included in read visibility, and entity-type
`manage` grants are included in spawn/type visibility.
- Added spawn-time initial grants for the new entity. Direct root-spawn
grants are allowed because the caller becomes the owner of the new
entity; broad parented-spawn grants require `manage` on the parent
before they can be delegated.
- Added an optional authorization hook for deployments that need custom
point decisions; webhook decisions are not injected into Electric
predicates and should be materialized into effective rows for shape
visibility.
- Enabled Electric subqueries in
`packages/agents-server/docker-compose.dev.yml` with
`ELECTRIC_FEATURE_FLAGS=allow_subqueries`.
## Permissions model
Principals are still defined outside Agents Server. The server consumes
the authenticated principal from the request context/header and stores
grants against either a concrete principal URL, such as
`/principal/user%3Aalice`, or a principal kind, such as `user`, `agent`,
`service`, or `system`. Native group management is intentionally out of
scope for this version; principal-kind grants cover the common
broad-access cases.
Type grants control type-level actions only. A type-level `spawn` grant
decides whether a principal may create an instance of a registered
entity type; type-level `manage` is used for type mutation and is
treated as a superset for type visibility/spawn checks. Type grants
never grant access to existing entity instances. New type creation is
open to authenticated principals so developers can register new entity
definitions, while mutation of an existing type definition remains
guarded by `manage`.
Entity grants control access to individual entity instances. Instance
access defaults to `created_by` ownership, plus explicit/effective
grants for verbs such as `read`, `write`, `delete`, `signal`, `fork`,
`schedule`, and parent `spawn`. Entity `manage` is treated as a superset
for point checks and visibility.
The permission verbs are intentionally granular. `write` covers content
mutation paths such as send, tags, inbox edits, attachments, and
event-source subscription edits. It does not imply `delete`, `signal`,
`fork`, `schedule`, parent `spawn`, or `manage`; those require their own
grants or `manage`. UI sharing presets like “read-only”, “read/write”,
and “full control” should expand into the appropriate set of granular
grants.
Spawn links the new entity into the permission graph:
- The caller must have a type-level `spawn` grant for the target type.
- If the spawn has a parent, the caller must also have `spawn` on that
parent entity.
- The new entity records `created_by` as the caller principal.
- Parent/child lineage is recorded so descendant grants can be
materialized for current and future descendants.
- Grants marked `copy_to_children` are copied into the child as direct
grants during spawn.
- The spawn body may include initial direct grants for the new entity.
- For parented spawns, broad or propagating initial grants require
`manage` on the parent. This covers `principal_kind` grants, `manage`
grants, `propagation=descendants`, and `copy_to_children=true`.
- Manifest-discovered shared-state links connect shared-state streams
back to the linked entity permissions.
## Key decisions
- Electric predicates use only `IN (SELECT ...)` subqueries with no
correlated outer references.
- Entity instance access defaults to `created_by` ownership plus
explicit/effective grants.
- Type grants are not used for instance visibility or instance access;
entity grants are the source of truth there.
- Workspace-wide sharing is represented as `subject_kind=principal_kind`
and `subject_value=user`. This assumes an Agents Server tenant/service
maps to a workspace boundary, and only workspace members can
authenticate as `user` principals in that tenant.
- Groups are not modeled in this version; deployments that need richer
policy can use the authorization hook and/or materialize decisions into
effective permission rows.
- Public or “anyone with the URL” sharing is a follow-up. The model
leaves room for a later `public`/`link` subject kind, synthetic
anonymous/link principals, or Cloud share-link records that resolve into
an authorization context with matching Electric visibility behavior.
- Expired grants are pruned/deactivated server-side rather than using
`now()` in Electric predicates.
- Pre-permission entity observation bridges are rebuilt after upgrade
because old bridge rows do not include principal attribution.
## Coverage
Added focused coverage for permission service behavior, route
middleware, durable stream access, shared-state authorization, Electric
SQL predicate generation, principal-scoped observation streams, runtime
registration grant payloads, spawn-time grant materialization and
parented-spawn delegation checks, and built-in Horton/Worker spawn
grants.
A local smoke run also registered a linked `/tmp` runtime, spawned an
entity as `user:test`, confirmed `user:other` was denied list/send/view
before grants, then confirmed `user:other` could list/send/view after
explicit `read` and `write` grants using
`ELECTRIC_AGENTS_SERVER_HEADERS` with `Electric-Principal`.1 parent 74d2341 commit 6434774
41 files changed
Lines changed: 3117 additions & 94 deletions
File tree
- .changeset
- packages
- agents-runtime
- src
- test
- agents-server
- drizzle
- meta
- src
- db
- routing
- utils
- test
- agents
- src/agents
- test
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
63 | 63 | | |
64 | 64 | | |
65 | 65 | | |
66 | | - | |
| 66 | + | |
67 | 67 | | |
68 | 68 | | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
69 | 74 | | |
70 | 75 | | |
71 | 76 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
510 | 510 | | |
511 | 511 | | |
512 | 512 | | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
513 | 516 | | |
514 | 517 | | |
515 | 518 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
| 29 | + | |
29 | 30 | | |
30 | 31 | | |
31 | 32 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1396 | 1396 | | |
1397 | 1397 | | |
1398 | 1398 | | |
1399 | | - | |
| 1399 | + | |
1400 | 1400 | | |
1401 | 1401 | | |
1402 | 1402 | | |
| |||
1607 | 1607 | | |
1608 | 1608 | | |
1609 | 1609 | | |
| 1610 | + | |
1610 | 1611 | | |
1611 | 1612 | | |
1612 | 1613 | | |
| |||
1647 | 1648 | | |
1648 | 1649 | | |
1649 | 1650 | | |
1650 | | - | |
| 1651 | + | |
1651 | 1652 | | |
1652 | 1653 | | |
| 1654 | + | |
| 1655 | + | |
| 1656 | + | |
| 1657 | + | |
| 1658 | + | |
| 1659 | + | |
| 1660 | + | |
| 1661 | + | |
| 1662 | + | |
| 1663 | + | |
| 1664 | + | |
| 1665 | + | |
| 1666 | + | |
| 1667 | + | |
| 1668 | + | |
| 1669 | + | |
| 1670 | + | |
| 1671 | + | |
| 1672 | + | |
| 1673 | + | |
1653 | 1674 | | |
1654 | 1675 | | |
1655 | 1676 | | |
1656 | | - | |
| 1677 | + | |
1657 | 1678 | | |
1658 | 1679 | | |
1659 | 1680 | | |
1660 | 1681 | | |
1661 | 1682 | | |
1662 | | - | |
1663 | | - | |
1664 | | - | |
| 1683 | + | |
| 1684 | + | |
| 1685 | + | |
1665 | 1686 | | |
1666 | 1687 | | |
1667 | | - | |
| 1688 | + | |
1668 | 1689 | | |
1669 | 1690 | | |
1670 | 1691 | | |
| |||
1695 | 1716 | | |
1696 | 1717 | | |
1697 | 1718 | | |
1698 | | - | |
| 1719 | + | |
1699 | 1720 | | |
1700 | 1721 | | |
1701 | 1722 | | |
| |||
1706 | 1727 | | |
1707 | 1728 | | |
1708 | 1729 | | |
1709 | | - | |
| 1730 | + | |
1710 | 1731 | | |
1711 | 1732 | | |
1712 | 1733 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
122 | 122 | | |
123 | 123 | | |
124 | 124 | | |
125 | | - | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
126 | 129 | | |
127 | 130 | | |
128 | 131 | | |
| |||
447 | 450 | | |
448 | 451 | | |
449 | 452 | | |
450 | | - | |
| 453 | + | |
| 454 | + | |
451 | 455 | | |
452 | 456 | | |
453 | | - | |
| 457 | + | |
454 | 458 | | |
455 | 459 | | |
456 | 460 | | |
457 | 461 | | |
458 | | - | |
| 462 | + | |
| 463 | + | |
459 | 464 | | |
460 | 465 | | |
461 | 466 | | |
462 | | - | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
463 | 471 | | |
464 | 472 | | |
465 | 473 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
413 | 413 | | |
414 | 414 | | |
415 | 415 | | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
416 | 423 | | |
417 | 424 | | |
418 | 425 | | |
| |||
1047 | 1054 | | |
1048 | 1055 | | |
1049 | 1056 | | |
| 1057 | + | |
1050 | 1058 | | |
1051 | 1059 | | |
1052 | 1060 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
659 | 659 | | |
660 | 660 | | |
661 | 661 | | |
| 662 | + | |
| 663 | + | |
| 664 | + | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
| 668 | + | |
662 | 669 | | |
663 | 670 | | |
664 | 671 | | |
| |||
698 | 705 | | |
699 | 706 | | |
700 | 707 | | |
| 708 | + | |
| 709 | + | |
| 710 | + | |
| 711 | + | |
| 712 | + | |
| 713 | + | |
| 714 | + | |
701 | 715 | | |
702 | 716 | | |
703 | 717 | | |
| |||
Lines changed: 2 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
93 | 93 | | |
94 | 94 | | |
95 | 95 | | |
96 | | - | |
| 96 | + | |
97 | 97 | | |
98 | 98 | | |
99 | 99 | | |
| |||
117 | 117 | | |
118 | 118 | | |
119 | 119 | | |
120 | | - | |
| 120 | + | |
121 | 121 | | |
122 | 122 | | |
123 | 123 | | |
| |||
Lines changed: 20 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
44 | 44 | | |
45 | 45 | | |
46 | 46 | | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
47 | 67 | | |
48 | 68 | | |
49 | 69 | | |
| |||
0 commit comments