Skip to main content

Glassbox

Your AI agent changed a file. Glassbox shows what happened—and gives you a guarded way back.

CI Latest release Python 3.11 or newer Apache 2.0 license

Run the demo · Read the security model · Download the latest release · Share feedback

Glassbox dashboard showing a verified receipt chain, risk labels, and rollback eligibility

Glassbox is a local-first action ledger for AI agents—a flight recorder with guarded rollback for eligible text-file changes. A bundled Claude Code hook, a command-line tool, or any integration submits actions to Glassbox after execution. Glassbox records a plain-language receipt, redacts common secret formats, assigns deterministic risk, and chains the receipt with HMAC-SHA256 so later tampering is detectable.

For file edits, creations, and deletions, integrations can submit the previous and resulting text. Glassbox encrypts the previous state and the diff locally and will undo the change only if the complete receipt chain verifies and live filesystem checks show that rollback will not overwrite newer work.

See it in action

Two-step Glassbox walkthrough: record an agent action, then restore the file while preserving history

  1. An integration records a file.write receipt with before and after text.
  2. Glassbox marks the receipt rollback-eligible and encrypts the previous state.
  3. A confirmed rollback runs live workspace, path, inode, ownership, mode, ACL, content, and chain checks.
  4. The renderer verifies the restored file bytes; the dashboard shows a new rollback receipt and a valid chain.

What you get

  • Readable receipts: agent, action, target, summary, timestamp, risk, and redacted metadata.
  • Tamper evidence: canonical HMAC-SHA256 chaining identifies the first broken receipt.
  • Guarded rollback: encrypted snapshots and conflict checks for eligible UTF-8 file edits, creations, and deletions.
  • Works with your agent: a Claude Code hook that records edits and shell commands with no code, plus a CLI and a dependency-free Python client.
  • Local operation: SQLite, local keys, a loopback-only API, and no required cloud service.
  • Inspectable behavior: an Apache-2.0 codebase, documented limitations, and security-focused tests.

Why this exists

AI agents can edit files, execute commands, and communicate on a user's behalf. Chat history is not an operational audit trail, and a generic backup does not answer whether a particular agent action is still safe to reverse. Glassbox gives each submitted action a durable receipt:

  • What happened? Agent, action, target, summary, timestamp, and metadata.
  • Was it dangerous? Deterministic low, medium, or high risk labels.
  • Was the log changed later? A keyed hash chain identifies the first broken receipt.
  • Can I undo it? Encrypted pre-change snapshots for eligible file writes.
  • Will undo destroy newer work? Current content and object identity must still match the recorded action.

Current capabilities

  • Claude Code hook that records file edits (with before and after text) and shell commands
  • Command-line recording, listing, verification, rollback, export, and retention
  • FastAPI ingestion and query API with paging and filters
  • Local dashboard with search, filters, session grouping, live updates, and an opt-in diff view
  • HMAC-SHA256 chained receipt log
  • Automatic redaction of common credential formats in summaries, targets, and metadata
  • Risk labels based on what an action touches, each with a recorded reason
  • SHA-256 before/after fingerprints
  • Fernet-encrypted rollback snapshots and diffs, stored as private files
  • Workspace boundary and path-traversal protection
  • Conflict-protected rollback of edits, creations, and deletions using Linux atomic filesystem operations
  • Signed retention: prune stored content without breaking verification
  • Exportable signed receipt JSON
  • Safe four-event demo timeline

Quick start

git clone https://github.com/jckm14/glassbox.git
cd glassbox
uv sync --locked --group dev

Create a populated local demo:

uv run glassbox demo \
  --workspace ./demo-workspace \
  --data-dir ./.glassbox-data

Demo generation uses exclusive file creation and refuses to overwrite an existing launch-plan.md. Actions that are not actually executed by the generator are explicitly labeled as synthetic sample receipts.

Start the dashboard:

uv run glassbox serve \
  --host 127.0.0.1 \
  --port 8765 \
  --workspace ./demo-workspace \
  --data-dir ./.glassbox-data

Open http://127.0.0.1:8765. Because the MVP has no remote authentication, the CLI accepts only 127.0.0.1 or localhost as bind hosts and refuses wildcard or non-loopback addresses.

Useful serve options:

  • --show-diffs lets the dashboard display the recorded change for a receipt. It is off by default, so the API serves hashes only.
  • --prune-older-than-days DAYS deletes stored snapshots and diffs older than DAYS at startup and every six hours. Receipts are kept.

Use it with Claude Code

Glassbox ships a Claude Code hook. Print the settings and merge them into .claude/settings.json in your project (or ~/.claude/settings.json for every project):

glassbox hook claude-code --print-config
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit|MultiEdit|NotebookEdit",
        "hooks": [{ "type": "command", "command": "glassbox hook claude-code", "timeout": 15 }]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit|MultiEdit|NotebookEdit|Bash",
        "hooks": [{ "type": "command", "command": "glassbox hook claude-code", "timeout": 15 }]
      }
    ],
    "PostToolUseFailure": [
      {
        "matcher": "Write|Edit|MultiEdit|NotebookEdit|Bash",
        "hooks": [{ "type": "command", "command": "glassbox hook claude-code", "timeout": 15 }]
      }
    ]
  }
}

glassbox must be on the PATH Claude Code uses; otherwise put the absolute path to the executable in command. From then on:

  • Each file edit becomes a file.write receipt, and each new file a file.create receipt, with the text before and after. Both can be undone.
  • Each shell command becomes a shell.exec receipt labeled by what the command does. Command output is not recorded.
  • Receipts carry the Claude Code session ID, so the dashboard and glassbox log --session can group them.
  • Widen the PostToolUse matcher (for example to *) to record other tools as well.

The hook works with or without a running server: it writes to the ledger directly, or hands the receipt to glassbox serve when the server owns the ledger. It never blocks or fails a tool call; problems are printed to stderr. The ledger defaults to ~/.glassbox; pass --data-dir (or set GLASSBOX_DATA_DIR) to keep one per project, and start glassbox serve with the same directory and the project as --workspace.

Before an edit runs, the hook holds the file's current text, unencrypted, in <data-dir>/pending/ (a mode-0700 directory). It is removed when the edit succeeds or fails; text left behind by an edit that never ran, such as one you denied, is removed by the next edit hook once it is an hour old. Content is recorded only for UTF-8 files up to 1 MB inside the workspace; other files get a receipt without content.

Command line

Every command takes --data-dir. Read commands work while the server is running; record and rollback are handed to the server when it owns the ledger.

Command Purpose
glassbox log List receipts; filter with --risk, --agent, --action, --session, --search, --reversible
glassbox show ID --diff Show one receipt and its recorded change
glassbox record Record an action from a script
glassbox verify Verify the chain; exit status 1 when it is broken
glassbox head Print the latest receipt hash
glassbox rollback ID Undo one eligible file change in --workspace
glassbox export -o receipts.json Write every receipt as JSON
glassbox prune Delete stored snapshots and diffs (--keep-last N, --older-than-days DAYS)

Receipt HMACs cannot show that the newest receipts were deleted. To detect that, save the output of glassbox head somewhere the agent cannot write, and later run glassbox verify --expect-head HASH: it fails if that receipt is no longer in the chain.

From Python, glassbox.client.GlassboxClient wraps the HTTP API using only the standard library:

from glassbox.client import GlassboxClient

GlassboxClient().record(
    agent="my-agent",
    action="file.write",
    target="notes/plan.md",
    summary="Updated the project plan",
    before_text=old_text,
    after_text=new_text,
)

Record an action

Want a runnable tool example first? Try the file-tool integration demo to verify a successful rollback and a refused rollback after a newer edit, using disposable files and no model credentials.

curl -X POST http://127.0.0.1:8765/api/events \
  -H 'Content-Type: application/json' \
  -d '{
    "agent": "my-agent",
    "action": "file.write",
    "target": "notes/plan.md",
    "summary": "Updated the project plan",
    "before_text": "old content",
    "after_text": "new content",
    "metadata": {"tool": "write_file"}
  }'

The API returns the receipt ID, risk label, content hashes, previous receipt hash, and receipt hash. Raw snapshots are never returned by the API.

Risk labels

Glassbox accepts arbitrary action names and labels each receipt from the words in the action name and from the target. The same submission always gets the same label, and the receipt records the reason.

Risk Examples
High Shell commands that delete recursively, escalate privileges, pipe a download into a shell, rewrite git history, change infrastructure, send data to a remote host, or touch credential files; sending, publishing, payment, and non-file delete actions; file changes to credential files or paths outside the workspace
Medium Other shell commands; file writes, creations, and deletions inside the workspace; configuration changes; any action Glassbox does not recognize
Low Read, search, and list actions; shell commands made only of read-only programs such as ls, grep, or git status

For a shell action, the command is read from metadata.command when present, otherwise from target. Labels are a triage aid based on pattern matching, not a sandbox: a command Glassbox labels medium can still be destructive. Commands longer than 20,000 characters, or with a single statement longer than 2,000, are labeled high without analysis.

Reversible actions

Action Supply Rollback does
file.write before_text and after_text Restores the previous text
file.create after_text only Moves the created file into a private recovery directory
file.delete before_text only Recreates the file, only if nothing exists at that path

Glassbox encrypts before_text for restoration, hashes after_text for conflict detection, and stores an encrypted diff. A recreated file gets mode 0600 unless the receipt's metadata.mode records the original permission bits (for example "0644"); group and other write bits and setuid bits are never restored. If metadata.workspace is present, rollback refuses to run in any other workspace.

The agent name glassbox and the actions file.rollback and snapshot.prune are reserved for receipts Glassbox writes itself; the API and CLI reject submissions that use them.

Verify receipts

curl http://127.0.0.1:8765/api/verify

Example:

{"valid": true, "event_count": 4, "broken_at": null}

If an authenticated receipt field or encrypted snapshot is modified so that its signed semantics change, or a snapshot file is missing without a signed prune receipt that names it, valid becomes false and broken_at identifies the first affected receipt. Semantically equivalent JSON reformatting is normalized during verification and is not treated as tampering.

Safe rollback

curl -X POST http://127.0.0.1:8765/api/events/3/rollback \
  -H 'Content-Type: application/json' \
  -d '{"confirm": true}'

Rollback succeeds only when all of these are true:

  1. The receipt represents a reversible file.write whose snapshot has not been pruned.
  2. Every parent component is opened beneath the configured workspace with directory descriptors and no-follow semantics.
  3. The final target is an existing UTF-8 regular file opened without following symlinks.
  4. Its current SHA-256, inode identity, owner UID/GID, file mode, and POSIX access ACL remain stable through the exchange boundary.
  5. The encrypted snapshot passes its own integrity check and decrypts correctly.

Rollback uses an atomic directory-fd-relative filesystem exchange, then validates both the installed restore-candidate inode and the displaced inode identity, type, content, owner UID/GID, file mode, and POSIX access ACL. It also rechecks workspace/parent identities against the no-follow workspace descriptor held from application startup. If the candidate, target entry, ownership/access policy, parent, or configured workspace identity changed—or synchronous validation fails after exchange—Glassbox exchanges the displaced entry back and returns HTTP 409. Restore candidates preserve the target owner UID/GID, mode, and POSIX access ACL while stripping inherited ACLs from the private recovery directory. Preservation intent is set before each exchange so an exception immediately after the syscall cannot trigger destructive cleanup. Restore candidates and retained inodes live inside a newly created mode-0700 recovery directory in the target's parent filesystem. The retained inode keeps its original ownership and file mode rather than being chmodded or chowned, avoiding metadata changes through hard-link aliases outside the workspace; access is restricted by the private directory. Once an exchange has occurred, Glassbox retains that inode—on success, conflict, or compensation—and reports its path, so late writes through a descriptor opened before exchange remain linked and recoverable. Rollback receipts carry a unique operation identifier generated before filesystem mutation: if persistence raises after commit, Glassbox reconciles the committed receipt rather than compensating the filesystem into a contradictory state. Review and remove recovery directories only after external writers are quiescent. A successful rollback produces a new receipt; Glassbox never silently rewrites history.

Rolling back a file.create or file.delete receipt applies the same chain, workspace, parent-directory, and no-follow checks, and uses an atomic no-replace rename instead of an exchange:

  • Created file: the file must still be a UTF-8 regular file whose content matches the receipt. Glassbox moves it into a new mode-0700 recovery directory beside it rather than unlinking it, then validates the moved inode and content. If validation or the rollback receipt fails, the file is moved back, unless something new already occupies its name, in which case it stays in the recovery directory and the response says where.
  • Deleted file: nothing may exist at the target path, including a directory or symlink. Glassbox writes the restored text to a private candidate, renames it into place without replacing anything, and validates the result. If a file appears at the path first, rollback returns HTTP 409 and leaves it untouched; if the rollback receipt cannot be written, the restored file is withdrawn.

API

Method Path Purpose
GET / Dashboard
GET /api/events Newest-first receipt page; limit, before_id, risk, agent, action, session, q, reversible
POST /api/events Record an agent action
GET /api/events/{id} One receipt
GET /api/events/{id}/diff Recorded change; 403 unless the server runs with --show-diffs
POST /api/events/{id}/rollback Confirm and apply a safe file rollback
GET /api/verify Verify the complete receipt chain
GET /api/stats Whole-ledger counts, agents, sessions, and chain head
GET /api/head Latest receipt hash
GET /api/export Every receipt with the verification result
GET /api/config Server version, workspace, and whether diffs are served
GET /docs Interactive OpenAPI documentation

GET /api/events returns events, the total number of matching receipts, and next_before_id to pass as before_id for the next page.

Security model

  • Glassbox binds to 127.0.0.1 by default.
  • The CLI refuses wildcard and non-loopback bind addresses; requests are restricted to loopback Host headers, and browser-originated mutations require a canonical same-origin http/https Origin when that header is present.
  • The dashboard has no CDN, analytics, or external font requests.
  • A restrictive Content Security Policy, frame protection, MIME sniffing protection, and no-referrer policy are applied to every response.
  • The data directory must be an owned mode-0700 real directory; existing symlinks or differently protected directories are rejected without chmodding their referents. The signing key and published database are owned, single-link mode-0600 regular files pinned by descriptors. Receipt readers and writers share a store lock so publication cannot close or recycle a descriptor while a reader is using it. Receipt writes occur in a serialized in-memory SQLite copy, are serialized into an unnamed mode-0600 file, and publish a finalized database snapshot with an atomic exchange. The previously published database inode is never mutated, so a hard link introduced at the commit boundary remains an unchanged historical snapshot rather than an alias Glassbox writes through.
  • Before snapshots and diffs are encrypted at rest with a key derived from the local receipt key. Each is stored as a mode-0600 file named by the SHA-256 of its ciphertext in a pinned mode-0700 snapshots/ directory; files are created exclusively and never rewritten, and a file that is missing, relinked, re-permissioned, or altered fails verification. Only a signed snapshot.prune receipt written by Glassbox excuses a missing file. Prune and rollback receipts name the receipts they affect by receipt hash, not row ID, so renumbering rows cannot move them onto another receipt.
  • Receipt responses expose hashes, not raw before/after content. Diffs are served only when the server is started with --show-diffs, and common credential patterns are redacted from them.
  • Common credential patterns are redacted before summaries, targets, and metadata are stored. A file receipt whose target was changed by redaction is not reversible.
  • Only one process writes to a data directory at a time. The server holds an advisory lock on writer.lock for its lifetime; one-shot commands take it briefly or send the write to the server. Read commands open the ledger read-only and never republish it.
  • Integrations cannot submit receipts under the reserved glassbox agent or the file.rollback and snapshot.prune actions, which decide whether a receipt counts as rolled back or pruned.
  • At application startup Glassbox opens the configured workspace component-by-component from the filesystem root without following symlinks and holds that descriptor for the application lifetime. Rollback traverses from the held descriptor, opens each parent directory without following symlinks, requires a no-follow regular-file target, and performs the Linux atomic exchange relative to pinned directory descriptors.
  • Newer human or agent edits cause rollback to fail safely with HTTP 409.

MVP limitations

  • It records events submitted by integrations and hooks. The Claude Code hook sees tool calls, not their side effects: a file changed or deleted by a shell command gets a shell.exec receipt with no snapshot.
  • Risk labels come from pattern matching on action names and command text. They can be wrong in both directions and are not an enforcement mechanism.
  • Redaction is defense in depth, not a substitute for avoiding secrets in telemetry.
  • The signing key is local. Third-party attestation and public-key signatures are future work.
  • Rollback currently supports UTF-8 text files only, up to 1 MB per snapshot. Diffs are not stored when the two texts together exceed 400,000 characters.
  • Every write still republishes the receipt database, so write cost grows with the number of receipts, though no longer with snapshot size.
  • Glassbox currently requires Linux: store initialization and database publication use directory descriptors, O_TMPFILE, and renameat2(..., RENAME_EXCHANGE), while rollback also requires a workspace filesystem supporting atomic exchange. Glassbox fails closed rather than falling back to unsafe pathname or replacement operations.
  • SQLite and an arbitrary workspace filesystem cannot share one crash-atomic transaction. Glassbox compensates detected receipt-write failures and reconciles post-commit exceptions, but it does not fsync every affected directory into a cross-filesystem durability protocol; a process or power failure in the file-to-ledger interval may require manual reconciliation.
  • Rollback exchanges intentionally retain private mode-0700 recovery directories containing plaintext workspace content. Retained inodes keep their original file mode to avoid mutating hard-link aliases; the directory supplies access protection. Integrations should review and remove the entire recovery directory once external writers are quiescent; the MVP does not garbage-collect it automatically, and glassbox prune does not touch it.
  • Receipt HMACs do not authenticate SQLite row IDs and, without an external chain-head anchor, cannot detect deletion of a valid suffix. glassbox head and glassbox verify --expect-head let you keep and check such an anchor by hand; public-key attestation and automatic external anchoring are future work.
  • Authentication is intentionally omitted. The bundled CLI therefore refuses non-loopback binding. Any separately engineered remote deployment requires authentication, CSRF protection, transport security, and explicit trusted-host configuration.

Development

uv sync --locked --group dev
uv run ruff format --check src tests scripts/render-launch-server.py scripts/validate-launch-assets.py scripts/publish-launch-assets.py
uv run ruff check src tests scripts/render-launch-server.py scripts/validate-launch-assets.py scripts/publish-launch-assets.py
shellcheck scripts/render-launch-assets.sh
uv run mypy src scripts/render-launch-server.py scripts/validate-launch-assets.py scripts/publish-launch-assets.py
uv run python -m compileall -q src tests scripts/render-launch-server.py scripts/validate-launch-assets.py scripts/publish-launch-assets.py
uv run pytest -q
uv build

See CONTRIBUTING.md before opening a pull request. Report suspected vulnerabilities privately according to SECURITY.md, not through public issues.

Support Glassbox

If Glassbox is useful to you or your team, sponsor its development. Your support helps fund maintenance, security-focused testing, documentation, and integration examples.

Read how sponsorship supports Glassbox. Bug reports, documentation improvements, and code contributions are welcome too.

License

Glassbox is licensed under the Apache License 2.0.

The API and rollback behavior are developed test-first. The suite covers recursive and compound sensitive-key redaction, valid and tampered receipt chains, malformed database values, encrypted-snapshot tampering, protected single-link key/database handling, complete rollback eligibility, atomic-exchange conflicts, same-content inode substitution, post-exchange interruption, post-commit receipt reconciliation, creation and deletion rollback races, snapshot-file substitution, signed pruning, legacy-ledger migration, reserved-name rejection, content-aware risk labels, the Claude Code hook, the single-writer lock, pinned-workspace identity, parent-directory and final-symlink substitution, hard-link alias safety, POSIX ACL preservation, private recovery directories, chain-gated rollback, receipt-failure compensation races, workspace escape attempts, concurrent receipt writers, DNS-rebinding/canonical-Origin defenses, non-loopback bind refusal, dashboard security, and safe CLI demo generation.

Metadata

Release files for glassbox-agent 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for glassbox-agent 0.2.0
File Size Uploaded
glassbox_agent-0.2.0.tar.gz 179.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for glassbox-agent 0.2.0
File Interpreter ABI Platform
glassbox_agent-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 249.6 kB

Release files / glassbox_agent-0.2.0.tar.gz

Download URL glassbox_agent-0.2.0.tar.gz
Size 179.4 kB
Tags Source
SHA-256 checksum
How to use checksums
4ff4d918682d8b77e3d7934357b69bea74856771a5e535e2521b81eb5633ed3a
BLAKE2b-256 checksum
How to use checksums
897fe4c84e0e4450d4a16e0ac3afca6666931bcb954ab43f07f7ee052aabe3dc
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 Oct 12, 2026.

Transparency log

Release files / glassbox_agent-0.2.0-py3-none-any.whl

Download URL glassbox_agent-0.2.0-py3-none-any.whl
Size 70.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7015be899e87fbd0aaf8d4da2478db54b696dd10b7cc9828248307c697103ce2
BLAKE2b-256 checksum
How to use checksums
c2496c43a05099270c624d8196cfcd3712076920a7047f8c28f5956188c177d3
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 Oct 12, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page