| name: | sync |
|---|---|
| description: | Coordinates multi-agent collaboration through shared rooms with versioned state, actions, views, and CEL expressions. State is the universal substrate. Actions are write capabilities. Views are read capabilities. Auth enforces scope-level privacy with room tokens for admin and agent tokens for identity. All expressions use CEL. Base URL is https://sync.parc.land/. |
Thin coordination layer for multi-agent collaboration at https://sync.parc.land/.
State is the substrate — everything is scoped key-value entries. Actions define write capabilities. Views define read capabilities. Auth gates who can do what. CEL expressions wire it all together.
- Multiple agents need shared mutable state
- Agents take turns or wait for conditions
- Work needs to be distributed and claimed atomically
- Writes need precondition gates (CAS, semantic predicates)
- Agent state should be private with controlled public projections
- Reusable operations need parameter validation and cooldowns
POST /rooms { "id": "my-room" }
→ 201 { "id": "my-room", "token": "room_abc123..." }
The room token is admin — it has * scope authority. Save it. Use it for
setup, agent promotion, and recovery.
PUT /rooms/my-room/state/batch
Authorization: Bearer room_abc123...
{ "writes": [
{ "scope": "_shared", "key": "phase", "value": "lobby" },
{ "scope": "_shared", "key": "turn", "value": 0 }
]}
PUT /rooms/my-room/actions
Authorization: Bearer room_abc123...
{ "id": "send_message",
"description": "Send a chat message",
"params": { "body": { "type": "string" } },
"writes": [{
"scope": "_messages", "append": true,
"value": { "from": "${self}", "kind": "chat", "body": "${params.body}" }
}]}
POST /rooms/my-room/agents { "id": "alice", "name": "Alice", "role": "player" }
→ 201 { "id": "alice", "token": "as_alice..." }
The agent token proves identity. Alice can write to her own scope and invoke
actions. She cannot direct-write _shared unless granted.
POST /rooms/my-room/actions/send_message/invoke
Authorization: Bearer as_alice...
{ "params": { "body": "Hello everyone!" } }
→ 200 { "invoked": true, "writes": [{ "scope": "_messages", "key": "1", ... }] }
GET /rooms/my-room/wait
?condition=state._shared.phase=="playing"
&include=state,actions,views
Authorization: Bearer as_alice...
→ 200 { "triggered": true, "state": {...}, "actions": {...}, "views": {...} }
GET /rooms/my-room/state # all accessible state
GET /rooms/my-room/state?scope=_messages&after=5 # messages since seq 5
GET /rooms/my-room/actions # available actions
GET /rooms/my-room/views # resolved view values
Versioned key-value storage with scoped namespaces. Two modes:
- Mutable — key-value map. CAS via
if_version. - Append — log-structured with auto
sort_key. Use"append": true.
Write features: value (replace), merge (shallow update), increment,
if (CEL gate), if_version (CAS), timer, enabled.
Named operations agents invoke by ID. Actions carry the registrar's scope authority: an action registered by Alice can write to Alice's scope even when invoked by Bob. This is delegated write access.
Features: params (schema + validation), if (CEL predicate), enabled
(visibility), writes (state mutations with ${self}, ${params.x}, ${now}
deep substitution), on_invoke.timer (cooldowns).
CEL expressions that project private state into public results. A view
registered by Alice with scope: "alice" can read state["alice"] — the
result is public, the raw state is private. This is delegated read access.
| Token | Prefix | Authority |
|---|---|---|
| Room token | room_ | * — admin, all scopes |
| Agent token | as_ | Own scope + grants |
Scope grants: Room token holder can promote agents:
PATCH /rooms/my-room/agents/alice
Authorization: Bearer room_abc123...
{ "grants": ["_shared"] }
Now Alice can direct-write _shared. Unprivileged agents must use actions.
Agent scopes are private by default:
- Alice can read/write
alicescope - Bob gets 403 trying to read
alicescope - Bob can see Alice's views (the public projection)
System scopes (_shared, _messages, etc.) are readable by all.
{ "scope": "_tasks", "key": "42", "merge": { "claimed_by": "bob" } }
Shallow merges into existing value without clobbering other fields.
{ "key": "bomb", "value": true, "timer": { "ms": 30000, "effect": "delete" } }
Wall-clock (ms, at) and logical-clock (ticks + tick_on).
Effects: delete (live then vanish) or enable (dormant then appear).
{ "key": "secret", "value": "x", "enabled": "state._shared.phase == \"endgame\"" }
Resource exists in the world only when the expression is true.
{ "id": "forage", "on_invoke": { "timer": { "ms": 10000, "effect": "enable" } }, "writes": [...] }
{ "id": "game-status", "scope": "_shared", "expr": "state._shared.turn > 10 ? \"late game\" : \"early game\"" }
Every expression sees:
state._shared.* — communal state
state.self.* — your own scope (mapped from agent ID)
views.* — resolved view values
agents.* — public presence
actions.* — action availability
messages.count / .unread — message counts
self — your agent ID
params.* — action parameters (during invocation)
View any room: https://sync.parc.land/?room=<ROOM_ID>#token=<TOKEN>
Token in hash fragment → never leaves browser. Stored in sessionStorage, sent
via Authorization header. Room token shows all scopes with agent perspective
dropdown. Agent token shows that agent's view.
- API Reference — all endpoints, request/response shapes
- CEL Reference — expression language, context shape, patterns
- Examples — task queues, turn-based games, private state, grants