Skip to main content

annals

Annals for AI agents. An agent publishes a document (markdown, html, an image, a video, a PDF, or a whole folder of renders) with one command and gets back a link; the owner opens their annals on a phone and finds everything their agents left for them, newest first, searchable, with a recycle bin.

pip install annals
annals publish report.md --session my-agent
# https://apps.example.com/annals/d/20261003-203301-quarterly-report-3f9a

Documents are plain files in a directory. The directory can be on this machine or on another one over ssh, and the page that serves it reads it directly, so publishing never redeploys anything. No database, no build step, no API token.

For agents

Load the shipped skill, annals-publish, and you have the whole protocol: one command, one link, put the link in your reply.

annals publish report.md                          # one document, prints its link
annals publish a.md b.md c.html --group "Review"  # one link for a set (the group link prints first)
annals publish ./renders                          # a folder: a gallery with inline images, video, audio
annals publish ./site_dir                         # an html page with its assets, as one document
annals publish clip.mp4                           # media plays inline (streamed, seekable)
some-command | annals publish - --title "Log"     # from stdin
annals ls "search words"                          # what is there, newest first
annals trash <id>  /  annals restore <id>         # the recycle bin

What each kind looks like on the page:

Published Shown as
a .md file rendered markdown, with a toggle to the source and a copy button; its relative images and links resolve to its own files
an .html file, or a folder with index.html the page itself, in a sandboxed frame
an image, video, audio file or PDF inline: image, player (byte-range streaming, so video seeks), PDF viewer
a folder with one page in it that page, with the other files below it
any other folder a gallery: image thumbnails (a page at a time), players, a file list; every file opens on its own with prev/next
anything else a download

A folder skips hidden files and caches (.*, __pycache__, *.pyc); --all-files keeps them.

With pip install 'annals[mcp]', the same operations are an MCP server: python -m annals.mcp.

Where documents go

annals configure --target tw:/root/.local/share/annals --base-url https://apps.example.com/annals

That writes ~/.config/annals/config.toml. target is a directory, or host:/path for a directory on another machine reached by ssh host (keys, no prompt; host can be an alias from ~/.ssh/config). ANNALS_TARGET and ANNALS_BASE_URL override it per shell. With no configuration, documents go to ~/.local/share/annals and the link points at a local annals serve.

Hosting the page

Inside an app platform (the usual case). Mount the API under the platform's prefix and let the platform's login gate it:

# server.py of the host app; the platform serves frontend/ at /annals/ and this at /api/annals
from annals.api import mk_api
app = mk_api(data_dir="/root/.local/share/annals")

and write the page shell once as the app's frontend:

annals page-shell --api /api/annals --base /annals > frontend/index.html

The shell only names those two paths; the page's script and style load from the API, so upgrading the package upgrades the page. Gallery thumbnails are made with Pillow when it is installed (cached under the data root's cache/), and fall back to the original image when it is not.

Standalone, for a machine with no platform:

pip install 'annals[server]'
ANNALS_DATA_DIR=~/.local/share/annals annals serve --host 127.0.0.1 --port 8765

The page is at http://127.0.0.1:8765/annals/, its API under /annals/api/. Auth is chosen by environment variables:

Situation Set Effect
Behind an existing login that exposes a who-am-I endpoint ANNALS_WHOAMI_URL, ANNALS_ALLOWED_USERS=me@example.com, optionally ANNALS_LOGIN_URL=/auth/login the browser's cookies are forwarded to that endpoint and the listed emails are allowed; anyone else is sent to the login page
No platform login ANNALS_BASIC_USER, ANNALS_BASIC_PASSWORD HTTP Basic
Localhost only nothing open

Python API

from annals import DocStore, publish
meta = DocStore("~/.local/share/annals").publish("report.md", tags=["q3"])
publish(["report.md"], tags="q3")["url"]          # same, through the configured target

annals.api.mk_api(...) is the mountable API; annals.api.mk_app(data_dir=..., authorizer=...) the standalone app.

Design notes

Flat store with tags and groups, not a hierarchy: agents from many projects do not share one, and every placement decision costs tokens. A document is docs/<id>/meta.json plus its files; the bin is trash/<id>/; a group is groups/<gid>.json. Ids are YYYYMMDD-HHMMSS-<slug>-<4 hex>, so a listing is already in time order and a URL says what it points to. The transport is a seam (annals.target): local directory, ssh, and later an http ingest endpoint. More in misc/docs/design.md.

Skills

gh skill install thorwhalen/annals annals-publish

The skill also ships inside the package at annals/data/skills/annals-publish/.

Metadata

Release files for annals 0.0.3

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

Source distribution (sdist)

Source distribution for annals 0.0.3
File Size Uploaded
annals-0.0.3.tar.gz 68.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for annals 0.0.3
File Interpreter ABI Platform
annals-0.0.3-py3-none-any.whl Python 3 none any Details

Total release size: 132.5 kB

Release files / annals-0.0.3.tar.gz

Download URL annals-0.0.3.tar.gz
Size 68.4 kB
Tags Source
SHA-256 checksum
How to use checksums
d572accbfe454580dc60cca5cb8360390564cc08d372f11cd1ae5ca365acb0b9
BLAKE2b-256 checksum
How to use checksums
5c85f16b0bbbb13c22c61c1bc3886b1ad4a4638a52f5427222b66418ccd30374
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / annals-0.0.3-py3-none-any.whl

Download URL annals-0.0.3-py3-none-any.whl
Size 64.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
39bcf5bd6f69051c772170c81938ad4f2e43e37445d4f730482f82cd77559158
BLAKE2b-256 checksum
How to use checksums
7297b5644d059ddec960ab1a79d33f953785ac941dc1e011e2ea8fb73082f5dc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

0.0.4

2 release files

This release

0.0.3 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