Skip to main content

Koshmana client

The client for Koshmana — a hypermedia, event-sourced data service living at https://koshmana.com. One small library, one CLI, one MCP server; no server code. If you are an agent who just landed on koshmana.com, this is how you talk to it.

What Koshmana is (the model in 60 seconds)

  • The log is truth. Everything is an activity (Activity Streams 2.0 JSON — Create, Update, Delete, …) appended to a collection. The append is the commit; the ack's sequence is the event's permanent position on the log. Nothing is ever edited or removed from the log — you change state by appending more activities.
  • Reads are projections. A read model folds the log into current state. Every read answer carries koshmana:watermark (how far behind truth the projection is). Pass an append ack's sequence back as min_sequence for read-your-writes.
  • Objects are dereferenceable URLs. An object's id resolves to its current state (GET /collections/{coll}/objects/{id}). On deployments with a public URL the id is an HTTPS URL you can GET directly.
  • Reads can be public; representations are content-negotiated. A collection declared public: true serves its objects to anyone with no bearer and Access-Control-Allow-Origin: *. Both object read doors are content-negotiated: Accept: text/html renders via the collection's template (if any); otherwise you get AS2 JSON. Writes always stay authenticated.
  • A collection can be a website. Declare site: true and its objects become files addressed by path; /{project}/{coll}/… serves them like a web server, and a deploy is just a batch of appends.

Install

pip install koshmana            # library + `koshmana` CLI
pip install "koshmana[mcp]"     # also the `koshmana-mcp` MCP server

Requires Python 3.13+. Runtime deps are tiny: httpx and pyyaml (plus mcp only for the optional MCP server).

Configuration — three environment variables

Env var CLI flag Default
Server KOSHMANA_URL --url http://127.0.0.1:8600
Token KOSHMANA_TOKEN --token dev-write-token
Actor KOSHMANA_ACTOR --actor (per command) https://koshmana.dev/actors/cli

To talk to production, set KOSHMANA_URL=https://koshmana.com, a real KOSHMANA_TOKEN, and a KOSHMANA_ACTOR that identifies you (every write's envelope actor must equal the actor your token is bound to). Reads on public collections need no token.

CLI quickstart

Output is YAML by default; add --json to see the exact wire format. Global flags (--url, --token, --json) work before or after the subcommand.

koshmana collections                          # what collections exist here
koshmana describe issues                      # a collection's full declaration
koshmana bindings journal                     # a collection's materialized bindings

# Read a declared read model (default entry: "recent")
koshmana get issues
koshmana get issues --slice by-status open    # a computed slice
koshmana get issues --where status=open --where priority=high
koshmana get issues --search '"exact phrase" -noise'
koshmana get issues --order priority --descending

# Append an activity (the commit). YAML or JSON, file or stdin.
koshmana append issues -f issue.yaml
echo '{"content": "quick note"}' | koshmana append notes -f -   # bare object → wrapped
# Anything carrying "actor" or "object" is sent as-is:
koshmana append issues -f envelope.yaml

# Declare a new collection via the catalog
koshmana declare -f collections/issues.json

# Blobs: upload a file, get its public content-addressed URL
koshmana upload photos ./logo.png
koshmana upload photos ./logo.png --type Image --name "Our logo"  # also appends a media object

# The live wire
koshmana tail issues                          # snapshot, then live activities, forever
koshmana tail issues --no-snapshot            # live only, from now

# Subscriptions
koshmana subscribe issues --webhook https://ex.io/hook   # durable webhook delivery
koshmana subscribe issues --webhook https://ex.io/hook --from-start
koshmana subscribe issues --tail              # mint a receive-only stream token
koshmana subscriptions issues
koshmana unsubscribe issues sub-abc123def456

# The raw log (truth) and tombstoning a mistake
koshmana log --from 0
koshmana invalidate issues 12 --reason "mistake"   # tombstone + compensation

# Capability grants (minting takes op `admin` on the tokens collection)
koshmana token mint --name ci --actor https://x.io/ci \
    --collections notes,issues --ops read,write \
    [--projects atlas,zeta | --projects '*'] [--expires ISO8601]
koshmana token mint --name owner --email you@example.com \
    --actor https://x.io/you --collections '*' --ops read,write,admin
koshmana token list
koshmana token revoke ci

A bare object (no actor/object key) is wrapped into an envelope (--type, default Create, attributed to --actor/KOSHMANA_ACTOR); anything already carrying actor or object passes through untouched. On append/upload the CLI also prints the created/updated object id — that is what you Update later (distinct from the activity id in the ack).

Python-client quickstart

from koshmana import Koshmana

k = Koshmana(url="https://koshmana.com", token="…")

# What exists
k.collections()
k.describe("issues")

# Append (the commit) — returns the ack {sequence, id, object, objects}
ack = k.append("notes", {
    "type": "Create",
    "actor": "https://koshmana.dev/actors/me",
    "object": {"type": "Note", "content": "hello"},
})

# Read a model; read-your-writes with min_sequence
page = k.query("issues", "recent", where={"status": "open"}, limit=50,
               min_sequence=ack["sequence"])
for item in page["orderedItems"]:
    ...

# Resolve one object by its id (O(1) lookup) — None if absent
obj = k.get("issues", "https://koshmana.com/koshmana/issues/…")

# Partial edit without dropping fields (fetch → shallow-merge → full Update)
k.update("issues", obj["id"], actor="https://koshmana.dev/actors/me",
         status="closed")

# Blobs: upload bytes, or upload + create a media object in one call
blob = k.upload("photos", png_bytes, "image/png")     # {hash, size, mediaType, url}

# Image transforms (Cloudflare Transformations, if the deployment has it):
#   k.image_url(ack['url'], width=400)  → .../cdn-cgi/image/width=400,format=auto/<blob-url>
#   resized + auto webp/avif off the immutable original, edge-cached
k.attach("photos", png_bytes, "image/png", as_type="Image", name="Logo")

# The stitch: snapshot, then the live wire, forever
for activity in k.tail("issues"):
    ...

# Declare a collection, tombstone an event, read the raw log
k.declare({"name": "notes", "schema": {"type": "object"}})
k.invalidate("issues", 12, reason="mistake")
k.log(from_seq=0, limit=200)

Raw HTTP essentials

Every call is authenticated with Authorization: Bearer <token> (except reads on public collections). All read routes also mount under /p/{project} to address a non-default project.

# Append an activity — this IS the commit
POST /collections/{coll}/events
Content-Type: application/json
{ "type": "Create", "actor": "…", "object": { … } }

# Invoke a read model (default entry "recent"); slices and bindings:
GET /collections/{coll}/recent?limit=50
GET /collections/{coll}/slices/{name}/{value}/recent
GET /collections/{coll}/{binding}/recent

# Resolve one object by id (fully percent-encode the id)
GET /collections/{coll}/objects/{object_id}
# …or dereference a minted URL id directly (public deployments):
GET /{project}/{coll}/{key}
#   Accept: text/html  → rendered via the collection's template (if any)
#   Accept: application/json (default) → AS2 JSON

# Blob upload (content-addressed; identical bytes dedup)
POST /collections/{coll}/blobs
Content-Type: image/png
<raw bytes>            →  { "hash", "size", "mediaType", "url" }

# The live wire (Server-Sent Events; `data:` lines carry each activity)
GET /collections/{coll}/live?cursor=0&watermark=-1

# The raw log (truth)
GET /admin/log?from_seq=0&limit=200

# Static-site collections (site: true) serve files by path
GET /{project}/{coll}/{path…}

Public collections (declared public: true) serve their reads and object URLs with no bearer and CORS *, so an <img>, a fetch(), or an EventSource on any origin just works. Writes are always authed.

MCP server

Shell-less hosts use the MCP server instead of the CLI — same client underneath. Install with the extra and run over stdio:

pip install "koshmana[mcp]"
koshmana-mcp        # reads KOSHMANA_URL / KOSHMANA_TOKEN / KOSHMANA_ACTOR

Tools: list_collections, describe_collection, query, append, declare, invalidate, read_log, and peek (watch the live wire briefly).

License

MIT — see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

koshmana-0.1.1.tar.gz (17.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

koshmana-0.1.1-py3-none-any.whl (20.1 kB view details)

Uploaded Python 3

File details

Details for the file koshmana-0.1.1.tar.gz.

File metadata

  • Download URL: koshmana-0.1.1.tar.gz
  • Upload date:
  • Size: 17.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.5.9

File hashes

Hashes for koshmana-0.1.1.tar.gz
Algorithm Hash digest
SHA256 f0c6d4a2dc8d8366ffe9c6f6f350a2aed99eb10065b387059c002cc9f415313f
MD5 16a6542c9863363a2ec5782fdef637a1
BLAKE2b-256 5b89de65aa7766b0e8dd71afd1378ea0f0ea07bb64a8223d454f6b8a5cb9e699

See more details on using hashes here.

File details

Details for the file koshmana-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: koshmana-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 20.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.5.9

File hashes

Hashes for koshmana-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 aaaa9e4a00b1c91308e580c037c521fe106dae2ab3bf95fc8ab1f515f28216df
MD5 8543b2e10f5b6b8208976a78135cddd5
BLAKE2b-256 dddd0fdce7c6499cb4e55676106974adba52f7fdd08fcd7d2ccd5c89223fafb8

See more details on using hashes here.

Release history Release notifications | RSS feed

0.1.2

2 files

This release

0.1.1 This release

2 files

0.1.0

2 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