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.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):// 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.1.tar.gz (21.3 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.1-py3-none-any.whl (25.9 kB view details)

Uploaded Python 3

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

Hashes for codot-0.1.1.tar.gz
Algorithm Hash digest
SHA256 c98e99d5ccb6c218f20d3ed54720c789993b2dc1a9fd5c7f534d8dcd563e53f7
MD5 60048d7b002ae418a0814257a17aecf3
BLAKE2b-256 e9c69f7d6c5d4ec196d4bc30d785e12d9651201ed91383b41fd74d276077cc29

See more details on using hashes here.

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

Hashes for codot-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 e94cb103274a2a7f8d29aa1d34f5170d172f5675b837eb923142c6148c0b078e
MD5 55a9517421932b94a22e680f6a38f09f
BLAKE2b-256 95c94142304f83477d4e351fb9f3d7f4d4ec29f2add91e0a76c3db7b6ccc6d9b

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