§17. Memory Garden
What this section covers
Named, ACL'd logical partitions of the fact store that sit inside a
scope. Memory Gardens add fine-grained, membership-based read/write
control on top of the coarse local | team | company | public
boundary. Basic ACL is owned by Spec-02-Scopes-and-ACL; advanced
behavior is staged here.
Status: Experimental / opt-in source package on main
Source material: Archived evolutionary spec snapshots. This page is the maintained Spec-X home for advanced Memory Garden ACL semantics.
Each subsection below shows the most recent normative text from the spec source. When earlier spec drafts also contained text for the same subsection, those revisions are collapsed under a Revisions accordion beneath it — open one to see what changed. Subsections that only appear in one draft render as plain text with no accordion.
§17.1 Motivation
The existing scope model (local | team | company | public) is a coarse, operator-level boundary. There is no way to create a named partition shared among a specific set of principals (e.g. "Project Atlas team: Alice + CTO agent + Codex assistant") without either:
Exposing facts
To the entire company scope.
Running a separate node
One node per partition is operationally expensive.
Memory Gardens fill this gap.
A garden is a named, ACL'd logical partition that sits inside a scope. It adds fine-grained, membership-based read/write control on top of the existing scope model.
§17.2 Garden Primitive
A garden is represented by two related structures: Garden (the container itself) and GardenMember (a principal's membership within it). The Garden record binds a human-readable slug to a fixed FactScope, ensuring all facts tagged with that garden share a single visibility tier. The scope field is set at creation time and cannot be changed — this invariant allows the ACL layer (§17.3) to reject scope-mismatched writes without consulting the facts table.
Membership uses a three-tier role model (GardenRole). Roles are coarse by design: fine-grained per-relation permissions would complicate the already-layered access control stack (scope + garden + federation), so the protocol defers to operators who need them to implement a proxy or plugin. The added_by field supports audit trails without requiring a separate history table.
Garden {
id: UUID // internal primary key
garden_id: URI // stigmem://{authority}/garden/{slug}
slug: string // URL-safe name; unique per node; e.g. "project-atlas"
name: string // display name
scope: FactScope // all facts in this garden MUST have this scope
description: string? // optional human-readable description
created_by: URI // entity_uri of creating principal; auto-added as admin
created_at: ISO 8601 UTC
}
GardenRole = "admin" | "writer" | "reader"
GardenMember {
garden_id: UUID // references Garden.id
entity_uri: URI // the member principal (human, agent, or system)
role: GardenRole
added_by: URI
added_at: ISO 8601 UTC
}
garden_id URI construction: stigmem://{node_authority}/garden/{slug}, where node_authority is the node's authority component from its node_id setting. The slug is lowercased and validated on creation (see §5.14).
§17.3 Access Control Rules
Garden ACL is checked at fact read and write time, in addition to (not instead of) existing scope enforcement.
Write access:
garden_id not providedadmin/writerreaderRead access:
A fact with garden_id set is returned in query results ONLY if the caller:
- Passes normal scope enforcement (has
readpermission). - Holds at least
readerrole in the garden.
Non-members cannot enumerate gardens they do not belong to.
When querying by garden_id filter, non-members receive HTTP 403.
When querying by entity/relation without a garden_id filter,
garden-tagged facts are silently excluded.
Admin operations:
§17.4 Garden Lifecycle
- Create: Any principal with
writepermission on the node can create a garden. Creator is automaticallyadmin. - Invite: Garden admin adds members via
POST /v1/gardens/:id/members. - Write facts: Members with
writeroradminrole includegarden_idinPOST /v1/facts. - Query: Members include
garden_idquery param inGET /v1/facts. - Retract: Garden writers/admins retract facts using the normal retraction pattern (assert with
confidence=0.0); must include the samegarden_id. - Delete: Garden admin deletes with
DELETE /v1/gardens/:id. Associated facts become orphaned (garden_id becomes a dangling reference; surfaced by lint, §14).
§17.5 Relationship to Scope
Gardens and scopes are orthogonal, layered controls:
Scope (coarse, node-global): local | team | company | public
Garden (fine, named subset): any subset of principals within a scope
A garden's scope field declares which scope its facts inhabit. This means:
Garden ACL trumps scope
A company-scoped garden's facts are not visible to every company-level reader; non-members are excluded.
Garden isolation prevents federation
A public-scoped garden's facts would normally federate, but garden isolation prevents federation (§6 invariant). Gardens are local-first by design.
Untagged facts unaffected
Facts without garden_id continue to use scope-only access control.
§17.6 Conventions
Relation namespace: Garden membership metadata is stored as system facts using the garden: prefix. The node writes these automatically when callers add or remove members via the garden management endpoints; they use source="system:stigmem" so that attestation checks (§18) can distinguish operator-managed membership from agent-authored assertions. The garden:member relation links the garden to a member entity, while garden:role:<entity_uri> records the member's permission level (reader, writer, or admin).
(entity=<garden_id>, relation="garden:member", value={type:"ref", v:<member_entity_uri>}, source="system:stigmem", ...)
(entity=<garden_id>, relation="garden:role:<entity_uri>", value={type:"string", v:"writer"}, ...)
System facts in garden: MUST NOT be modified directly by callers.
These system facts are written automatically on membership changes.
The garden: prefix is reserved in the namespace registry (§9.1).