barrel_spaces
The barrel agent layer: spaces, capability tokens, sessions, handoffs.
A space is a barrel database created through this layer. Sharing context
means holding a capability for the space; every barrel feature
(documents, search, channels, timeline, per-database encryption, sync)
works inside one unchanged. Space metadata lives in the _barrel_spaces
registry database; space names are generated so labels never constrain
database naming.
You need this when several agents work on the same data and you want to hand context between them without copying it, or hand out scoped, revocable access to one space.
Documentation | HexDocs | Repository
Spaces
create_space/1 returns a space handle, #{id := SpaceId, db := Db}. The
lifecycle calls take the id; everything else takes the handle.
{ok, #{id := SpaceId, db := Db} = Space} =
barrel_spaces:create_space(#{label => <<"experiment-42">>}),
{ok, _} = barrel:put_doc(Db, #{<<"id">> => <<"note">>, <<"v">> => 1}),
ok = barrel_spaces:close_space(SpaceId),
{ok, Space} = barrel_spaces:open_space(SpaceId).
Space databases open through Barrel's database lifecycle manager
(barrel_dbs), so idle spaces close automatically and hundreds of
ephemeral spaces stay cheap. Per-space encryption specs give each
space its own key (agent isolation); like all barrel runtime config,
the spec must be passed again on every open.
Capability tokens
A capability is an opaque random token verified against a grant document in the registry. Only a SHA-256 of the token is stored, so nothing on disk can mint access, and revoking a grant takes effect immediately.
%% The token is returned once, here. Only its SHA-256 is stored.
{ok, Token, _Grant} = barrel_caps:grant(SpaceId, #{rights => [read, write],
subject => <<"agent-b">>}),
%% The bearer proves it holds `write' on this space.
{ok, _Ctx} = barrel_caps:verify(Token, SpaceId, write),
{ok, _Grants} = barrel_caps:list(SpaceId),
ok = barrel_caps:revoke(Token).
The token is shown once, when you mint it. verify/3 fails closed: unknown,
tampered, revoked, expired, wrong-space, and under-privileged tokens all return
an error, and none of them echo token material. barrel_server uses the same
tokens to scope its HTTP routes.
Sessions
A session is a TTL-bounded conversation inside a space: messages, arbitrary key/value data, a summary, and pinned context.
{ok, Sid} = barrel_session:create(Space, #{agent => <<"agent-a">>,
ttl => 3600}),
{ok, _Rev} = barrel_session:add_message(Space, Sid,
#{role => <<"user">>,
content => <<"index the corpus">>}),
{ok, _Messages} = barrel_session:get_messages(Space, Sid),
{ok, _} = barrel_session:set_data(Space, Sid, <<"cursor">>, 42),
{ok, 42} = barrel_session:get_data(Space, Sid, <<"cursor">>),
{ok, _ExpiresAt} = barrel_session:touch(Space, Sid),
ok = barrel_session:delete(Space, Sid).
ttl defaults to the space's session_ttl, or one hour. touch/2 extends it.
set_summary/3, pin_context/3, unpin_context/3, and list_pinned/2 keep the
long-lived context a session should not lose when its messages are compacted.
Handoffs
A handoff passes a task and its space from one agent to another. Creating one mints a capability for the space, so the acceptor gets access by presenting the token, not by being granted anything up front.
{ok, #{handoff_id := _Hid, token := HandoffToken}} =
barrel_handoff:create(Space, #{task_name => <<"reindex">>,
from_agent => <<"agent-a">>,
to_agent => <<"agent-b">>,
context => <<"corpus is loaded">>,
rights => [read, write]}),
%% On the other side, with only the token:
{ok, #{space := SharedSpace, session := Sid, handoff := _H}} =
barrel_handoff:accept(HandoffToken, #{agent => <<"agent-b">>}),
%% complete/2 takes the token, not the handoff id, and revokes its grant.
{ok, _} = barrel_handoff:complete(HandoffToken,
#{result => <<"12k docs indexed">>}).
accept/2 opens the shared space and creates a fresh session in it; the
from-agent's context is read in place, never copied. chain/2 walks a handoff
back through the agents that touched it.
A runnable version of this whole flow, compiled and executed by
barrel_agent_example_SUITE, lives at
examples/agent_layer.erl.
Over HTTP
barrel_server exposes all of this at /spaces, /spaces/:space/grants,
/spaces/:space/sessions, and /handoffs, authenticated by the same capability
tokens. See its README.