blackboardxray
Self-hosted observability for blackboardx runs. One server, one database, one interface. An application reaches it by naming an endpoint and a token, and nothing else about the application changes.
A blackboard run is a set of agents writing to a shared board under an admission rule. When one goes wrong the board tells you what was written, and nothing tells you what the run did about it: who was notified, who acknowledged, what admission refused, or which delivery never arrived. This records that.
Two containers and nothing else. No cache, no queue, no object store, and no secret for you to generate.
Contents
- Why it exists
- Run it
- Observe a run
- What is recorded
- Who can see what
- What the interface shows
- Development
- Self-hosting reference
- Security and Contributing
Why it exists
blackboardx makes the record durable and leaves the run in the process. The
store holds the regions, the contributions, the premise versions, the sequence,
the two deadlines and the outcome. It does not hold who was notified, who
acknowledged, what admission refused, or which delivery failed, and the
library's logging rule forbids logging any of them on the grounds that the agent
already saw them.
So the record is observable and the run is not. That is what this is for.
Run it
git clone https://github.com/MoeinRoghani/blackboardxray
cd blackboardxray
docker compose up -d
Open http://localhost:8900. The first screen makes your account, an organization, a project, and a key to send with. Everybody after you arrives by an invitation link, because this platform has no mail server and does not want one.
Two containers and nothing else. No cache, no queue, no object store, and no secret to generate: sessions are opaque identifiers held in Postgres and every credential is stored as a digest. Postgres is the only thing holding state, so it is the only thing to back up.
Without Docker:
pip install 'blackboardxray[server]'
createdb blackboardxray
export BLACKBOARDXRAY_DATABASE_URL=postgresql://localhost/blackboardxray
blackboardxray serve
Every environment variable, the proxy headers that matter, backup, upgrading and the volume ceiling are in docs/self-hosting.md.
Observe a run
from blackboardxray import Xray
xray = Xray(endpoint="http://localhost:8900", token="bxr_...") # the key from setup
model = xray.create_model(
board_id="incident-4471",
store=SqliteStore("incidents.sqlite3"),
regions=[Level("platform"), Premise("window")],
premises={"window": ["20:00", "22:00"]},
agents=[Agent(name="ocp", notify=investigate)],
limits=RunLimits(wall_clock=timedelta(minutes=10), idle=timedelta(seconds=30)),
)
Every argument is the one blackboard.create_model takes and means the same
thing. What comes back is the library's own model. The library is not modified
and is not asked to grow a hook.
An agent deployed as its own service wraps its board instead:
board = xray.agent_board(BoardClient(base_url=..., board_id=..., agent="ocp"))
What is recorded
Ten kinds, in the blackboard's own vocabulary.
| Kind | When |
|---|---|
run.opened |
A model was created, with its regions, premises and limits |
agent.registered |
An agent joined, at creation or afterwards |
write.admitted |
A level write passed admission and reached the board |
write.refused |
A write was refused, with the cause and the rule's reason |
write.conflicted |
A premise write named a version no longer current |
premise.set |
A premise write reached the board |
notification.dispatched |
A notification left for one agent |
notification.acknowledged |
An agent reported it had stopped |
notification.failed |
Delivery raised, so the agent never received it |
run.closed |
The run closed, with its outcome and who did not finish |
A contribution's content is recorded, truncated past 4kB. A deployment whose
contributions may not leave the process passes content_limit=0, which records
the size and the shape and none of the content.
Who can see what
An organization owns projects. A person is a member of an organization with a
role, and a role on one project replaces it in either direction: it raises a
member to admin on the project they run, and it is how somebody given none at
the organization gets exactly one project and nothing else.
| Role | May |
|---|---|
owner |
Everything, including deleting the organization |
admin |
Projects, keys and people. Cannot touch owners |
member |
Read every run, including what was written |
viewer |
Read what happened, and not what was written |
none |
Nothing, except a project they were given explicitly |
A viewer is the one worth explaining. A contribution is your application's own data and may hold anything it is allowed to hold, so the lowest role that can be handed out sees that four kilobytes of an object were admitted at sequence nine, and not what was in it.
What the interface shows
blackboardxray serve serves the interface at the same address as the API.
Boards, at /p/<project>, is a chart of runs opened per interval over the record of
them. Clicking a bar narrows the table to the runs that bar counted; the facet
strip between them narrows by outcome, by agent, or to the runs an agent did
not finish, and each facet carries the count it would leave. Selecting a board
opens a panel beside the list with what happened inside it: which agent was
refused, on which region, and what the rule said.
A board, at /p/<project>/boards/<board_id>, reads three ways. Graph draws what
wrote to what and who was told, with a broken edge where a notification never
arrived. Events is the same run as a table and is the record. Board is what
was actually written. The run's own clock stays along the bottom of all three.
Every filter, the time window, the selected board and the chosen view are in the address, so a view is a link.
Reading needs an account. Ingestion needs a key. The two are separate doors: an application holds a key and may only write, and a person holds a session and may only read.
Development
make setup
make verify
make verify needs a database and skips every test that touches one without
it, so name one:
createdb blackboardxray_test
BLACKBOARDXRAY_TEST_DSN=postgresql://localhost/blackboardxray_test make verify
examples/seed.py runs real models through the real client. examples/backfill.py
posts a day of fabricated traffic over the ingest API, so the interface can be
looked at at the volume it is for; it is a fixture and nothing in a deployment
runs it.
Documentation
| Self-hosting | Every environment variable, proxy headers, backup and restore, upgrading, and the volume ceiling |
| Architecture | The two halves, the wire between them, and what runs in the background |
| Decisions | Why one database, why sessions rather than tokens, why links rather than email |
| Security | How credentials are stored, what is in scope, and what this deliberately does not defend against |
| Contributing | The change flow, the gates, and the two rules about a pull request body |
| Design system | The token pipeline, the depth ladder, and why a screen may not set a value |
| Limits | What the client does when the platform is down or slow |
Status
Version 0.x. The public surface may still change, and a breaking change carries
a ! and a migration note in its release. The wire format between the client
and the server is versioned separately and decodes tolerantly in both
directions, so a client and a server from different releases work together.
License
Apache-2.0. See LICENSE.
Metadata
Release files for blackboardxray 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| blackboardxray-0.1.0.tar.gz | 636.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| blackboardxray-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.0 MB
Release files / blackboardxray-0.1.0.tar.gz
| Download URL | blackboardxray-0.1.0.tar.gz |
|---|---|
| Size | 636.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
39d726fad3aca1712d82530239f1979ceac2ecb8d52bdca180add65486eac09a
|
|
BLAKE2b-256 checksum How to use checksums |
56f4244fde7932dcd0917820e17dbfddb9f2a4a1f4978fb3cd4b5af02679627c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.
Transparency logRelease files / blackboardxray-0.1.0-py3-none-any.whl
| Download URL | blackboardxray-0.1.0-py3-none-any.whl |
|---|---|
| Size | 398.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ee2bf3c26c103d0a3472cf4057ad26cc9a0bd977a2fbd33911562d7b2ba31d9d
|
|
BLAKE2b-256 checksum How to use checksums |
c3457c41635df394f1fbd06077ca4a2d9fc124f1e546cd97b5eb048efd5fcc6b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.
Transparency log