CQRS-URL Platform - Commands and Queries as URL-addressable resources
Project description
codot is CQRS-URL Platform
AI Cost Tracking
- ๐ค LLM usage: $0.1500 (1 commits)
- ๐ค Human dev: ~$100 (1.0h @ $100/h, 30min dedup)
Generated on 2026-04-22 using openrouter/qwen/qwen3-coder-next
Commands and Queries as URL-addressable resources. Operate on arbitrary data over pluggable protocols (http://, https://, file://, data:, โฆ), with runtime JSON-Schema validation and policy-based access control โ no DTOs, no codegen, no per-command type churn.
This is the reference implementation of the design discussed in the accompanying articles:
- CQRS decoupled from data models (bytes + Struct envelope)
- Command/Query as a URL resource (
PUT /commands/converttojson) - Required schemas at runtime (JSON Schema at
schema_uri) - Controlling who can do what on which URI (policy engine with URL globs)
Quick start
# 1. Build and run everything
make build
make up
# 2. Issue a token (admin)
make token
# 3. Open the playground
# โ http://localhost:8000
#
# Sign in with admin/admin, alice/alice (analyst), or bob/bob (user).
# Hit one of the preset buttons: CSV โ JSON, render posts, pipeline.
# 4. Smoke-test the API
make test
Endpoints
| Method | Path | Purpose |
|---|---|---|
| GET | /health |
liveness probe |
| GET | /catalog |
public catalog of commands/queries/protocols |
| POST | /auth/token |
issue a dev JWT |
| GET | /auth/me |
current principal |
| GET | /commands |
list commands (auth) |
| PUT | /commands/{name} |
run a command |
| GET | /queries |
list queries (auth) |
| POST | /queries/{name} |
run a query |
| GET | /docs |
OpenAPI / Swagger UI |
Bundled commands
| Name | Purpose |
|---|---|
fetch |
read a resource from any protocol and return its raw bytes (base64) |
converttojson |
fetch + transform CSV/text/XML to JSON (+ optional schema validation) |
converttoxml |
fetch JSON/CSV and emit XML |
converttocsv |
fetch JSON list-of-objects and emit CSV |
converttobase64 |
base64-encode any resource (useful for PDFs, images) |
render |
Jinja2 template โ HTML page (data from URI or inline) |
pipeline |
chain other commands; use "$previous.output" as a URI reference |
Adding your own command is three steps: subclass Command, register it, optionally add a policy rule.
Bundled queries
| Name | Purpose |
|---|---|
from-url |
fetch one or more URIs and return them in a list |
introspect |
list commands, queries, protocols |
Protocols
| Scheme | Notes |
|---|---|
http, https |
standard fetch via httpx, size-limited |
file:// |
local reads limited to ALLOWED_LOCAL_ROOTS (default: /data, /schemas) |
data: |
RFC 2397 inline payloads, base64 or percent-encoded |
Adding a new protocol (e.g. s3://, ftp://, sqlite://) is a matter of writing a class with a scheme attribute and an async fetch(uri) method, then registering it in protocols/__init__.py.
Policy
Policies are loaded from api/policy/rules.yaml at startup. Each rule matches by role and lists glob patterns of allowed command/query names, URIs and schema URIs. Reload without rebuild: edit the file and restart the container (make restart).
Three built-in roles:
- admin โ everything
- analyst โ all commands/queries, all
http(s)://andfile:///data,file:///schemas - user โ only
fetch,converttojson,converttobase64,renderand public queries, only againsthttp://cqrs-data/*,https://public-*,file:///data/public/*,data:*
See also: api/policy/__init__.py (the engine), api/auth/__init__.py (JWT issuance), api/main.py (enforcement point).
Layout
.
โโโ api/ FastAPI service
โ โโโ commands/ one file per command
โ โโโ queries/ one file per query
โ โโโ protocols/ pluggable URI fetchers
โ โโโ policy/ RBAC engine + rules.yaml
โ โโโ validators/ JSON Schema over arbitrary URIs
โ โโโ auth/ JWT issuance + FastAPI dependencies
โ โโโ models.py envelope (CommandRequest/Response)
โ โโโ config.py env-based settings
โ โโโ main.py HTTP layer
โโโ frontend/ nginx + static playground (HTML/CSS/JS)
โโโ schemas/ JSON Schemas served at http://schemas/
โโโ sample-data/ demo data served at http://cqrs-data/
โโโ tests/
โ โโโ smoke.sh curl-based end-to-end tests
โ โโโ test_policy.py pytest unit tests
โ โโโ test_protocols.py
โโโ articles/ status articles (Markdown, for WordPress)
โโโ docker-compose.yml
โโโ Makefile
Adding a command
- Create
api/commands/my_thing.py:
from . import Command
from models import CommandRequest, CommandResponse
class MyThingCommand(Command):
name = "mything"
description = "Short sentence."
input_hint = {"input_uri": "...", "meta.foo": "..."}
async def execute(self, request: CommandRequest) -> CommandResponse:
# ... do work, return CommandResponse(payload_b64=..., mime=..., meta=...)
- Register it in
api/commands/__init__.py::register_default_commands. - Optionally add an entry in
rules.yamlif you want non-admins to call it.
That's the whole loop.
Environment
Copy .env.example โ .env and adjust. Key variables:
JWT_SECRETโ must be โฅ 32 chars in productionACCESS_TOKEN_EXPIRE_MINUTESโ default 60ALLOWED_LOCAL_ROOTSโ comma list, default/data,/schemasFETCH_MAX_BYTESโ size cap for fetched resources (default 50 MiB)
License
Licensed under Apache-2.0.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file codot-0.1.1.tar.gz.
File metadata
- Download URL: codot-0.1.1.tar.gz
- Upload date:
- Size: 21.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c98e99d5ccb6c218f20d3ed54720c789993b2dc1a9fd5c7f534d8dcd563e53f7
|
|
| MD5 |
60048d7b002ae418a0814257a17aecf3
|
|
| BLAKE2b-256 |
e9c69f7d6c5d4ec196d4bc30d785e12d9651201ed91383b41fd74d276077cc29
|
File details
Details for the file codot-0.1.1-py3-none-any.whl.
File metadata
- Download URL: codot-0.1.1-py3-none-any.whl
- Upload date:
- Size: 25.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e94cb103274a2a7f8d29aa1d34f5170d172f5675b837eb923142c6148c0b078e
|
|
| MD5 |
55a9517421932b94a22e680f6a38f09f
|
|
| BLAKE2b-256 |
95c94142304f83477d4e351fb9f3d7f4d4ec29f2add91e0a76c3db7b6ccc6d9b
|