Skip to main content

CQRS-URL Platform - Commands and Queries as URL-addressable resources

Project description

codot is CQRS-URL Platform

AI Cost Tracking

PyPI Version Python License AI Cost Human Time Model

  • ๐Ÿค– LLM usage: $0.9000 (6 commits)
  • ๐Ÿ‘ค Human dev: ~$344 (3.4h @ $100/h, 30min dedup)

Generated on 2026-04-23 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):// and file:///data, file:///schemas
  • user โ€” only fetch, converttojson, converttobase64, render and public queries, only against http://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

  1. 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=...)
  1. Register it in api/commands/__init__.py::register_default_commands.
  2. Optionally add an entry in rules.yaml if 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 production
  • ACCESS_TOKEN_EXPIRE_MINUTES โ€” default 60
  • ALLOWED_LOCAL_ROOTS โ€” comma list, default /data,/schemas
  • FETCH_MAX_BYTES โ€” size cap for fetched resources (default 50 MiB)

License

Licensed under Apache-2.0.

Project details


Download files

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

Source Distribution

codot-0.1.7.tar.gz (21.7 kB view details)

Uploaded Source

Built Distribution

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

codot-0.1.7-py3-none-any.whl (26.1 kB view details)

Uploaded Python 3

File details

Details for the file codot-0.1.7.tar.gz.

File metadata

  • Download URL: codot-0.1.7.tar.gz
  • Upload date:
  • Size: 21.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for codot-0.1.7.tar.gz
Algorithm Hash digest
SHA256 10425c6c17d58b29b0d2587f50e9e6a6fac15e2542d44437cb13ee4a00ad0460
MD5 3e51c5d5ca25f7eeff0a4363eda40a95
BLAKE2b-256 a2f17de830f412f49ee942a4f741cbc1b0a153562b4156135527819ec5c9dc4a

See more details on using hashes here.

File details

Details for the file codot-0.1.7-py3-none-any.whl.

File metadata

  • Download URL: codot-0.1.7-py3-none-any.whl
  • Upload date:
  • Size: 26.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for codot-0.1.7-py3-none-any.whl
Algorithm Hash digest
SHA256 6983399d3c3e88326e9576bec43778d73483259574166ce80b4fffdfc0399f45
MD5 52cb48f84638a51b487cb0c2919aaeba
BLAKE2b-256 c609aa8a96fc5ed818a6442a185115895500437d709e0c6f1998e813596f6077

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page