arcana-core
Version, currently 0.16.024 versions
- 0.18.0latestOct 12, 2026
- 0.17.1not indexedOct 12, 2026
- 0.17.0not indexedOct 12, 2026
- 0.16.1not indexedOct 12, 2026
- 0.16.0Oct 8, 2026
- 0.15.0not indexedOct 8, 2026
- 0.14.0Jul 27, 2026
- 0.13.0not indexedJul 27, 2026
- 0.12.0not indexedJul 27, 2026
- 0.11.0not indexedJul 27, 2026
- 0.10.0not indexedJul 27, 2026
- 0.9.1not indexedJul 27, 2026
- 0.9.0not indexedJul 27, 2026
- 0.8.0not indexedJul 27, 2026
- 0.7.1not indexedJul 27, 2026
- 0.7.0not indexedJul 27, 2026
- 0.6.0not indexedJul 27, 2026
- 0.5.2not indexedJul 27, 2026
- 0.5.1not indexedJul 27, 2026
- 0.5.0not indexedJul 27, 2026
- 0.4.0not indexedJul 27, 2026
- 0.3.0not indexedJul 27, 2026
- 0.2.0not indexedJul 27, 2026
- 0.1.0not indexedJul 27, 2026
github.com/trans/arcana-core
Core Data Structures and Utilities for Arcana
Installation
# Add this to your shard.yml
dependencies:
arcana-core:
github: trans/arcana-core
version: ~> 0.16.0Then run:
shards installshard.yml
- Crystal
>= 1.19.1- License
- MIT
- Author
- Thomas Sawyer
Dependencies
This version declares no dependencies.
README
This README is the one indexed from the repository at its latest ref, not from the tag for this version.
arcana-core
The mechanics of the Arcana agent bus,
for Crystal: envelopes, mailboxes, the directory, the bus itself, and the
building blocks for a service on it (Toolset, Service) or a client of
it (Client). The daemon, its MCP bridge and the session hooks live in
arcana; this shard is what both sides build on.
Installation
Add the dependency to your shard.yml, then run shards install:
dependencies:
arcana-core:
github: trans/arcana-core
Writing a service
A Toolset puts several tools behind one bus address. Over a Client, it
runs in your own process and joins the daemon's bus:
require "arcana-core"
client = Arcana::Client.new(
url: "ws://localhost:19118/bus",
address: "mj:camera", name: "mj:camera",
description: "Image generation behind a camera abstraction",
kind: Arcana::Directory::Kind::Service,
)
ts = Arcana::Toolset.new(client: client, max_concurrent: 16)
ts.tool("shoot", "Take a picture") do |data|
JSON::Any.new({"image" => JSON::Any.new(shoot(data))})
end
ts.run # joins the bus, then serves until the connection closes
Callers send {"tool":"shoot", ...} to the address; {"tool":"help"}
returns the manifest. Toolset#start alone only installs the handler, so
use run (or start then client.connect), and to serve several
toolsets from one process, run all but the last in fibers.
Requests run concurrently
Each request runs in its own fiber, at most max_concurrent at once
(default 8, nil for no cap). Set the cap to what your handlers' backend
can take. Two things follow from running at the same time:
A toolset in front of several backends wants lanes. One cap for the whole toolset lets requests queued for a slow backend crowd out ones another backend could serve. Give each backend a lane with its own cap, and say which lane a request takes, fixed per tool or worked out from its data:
ts.lane("runware", max_concurrent: 3)
ts.lane("google", max_concurrent: 6)
ts.tool("shoot", "Take a picture",
lane: ->(data : JSON::Any) { backend_for(data) }) do |data|
# ...
end
A request waits for its lane before it takes one of the toolset's slots,
so a busy lane holds up no one else; max_concurrent stays the overall
backstop.
State that handlers share needs exclusive. Even though fibers share
one thread, any IO can pause a handler partway through while another runs.
An append-only log, a counter read and written around a call, or a cached
client can be corrupted that way. A handler's optional second argument
gives it exclusive, which runs one block at a time per key and leaves the
rest of the handler concurrent:
ts.tool("shoot", "Take a picture") do |data, ctx|
image = shoot(data) # concurrent: the slow part
ctx.exclusive(:ledger) { ledger << cost } # one at a time: the shared write
JSON::Any.new(image)
end
Code outside handlers can share the same locks with ts.exclusive(:ledger).
Service, a single-purpose service registered in-process, has the same
max_concurrent: and exclusive.
Development
crystal spec
crystal docs # API docs in docs/
Contributors
- Thomas Sawyer - creator and maintainer
Documentation
Built from the current release. The first visit to a release nobody has asked for starts its build.
Links
This release
- Version
0.16.0- Tagged
- Oct 8, 2026
- Commit
830bc7622cb6- Crystal
>= 1.19.1- Indexed
- yes
Dependents
Repository
github.com/trans/arcana-core
Metadata
- Created
- Aug 12, 2026
- Updated
- Oct 13, 2026
- Synced
- Oct 13, 2026
- Versions
- 24