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: ~$200 (2.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.2.tar.gz (21.4 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.2-py3-none-any.whl (25.9 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: codot-0.1.2.tar.gz
  • Upload date:
  • Size: 21.4 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.2.tar.gz
Algorithm Hash digest
SHA256 addc5277f4ad949dc0ede02695b69fc6abeeb04309c5546d9ce61aa5431cdf66
MD5 ff1fa35658bb68bc014c7a51eb1f1865
BLAKE2b-256 0540c63c24f3f7a59df50e38c086d6306dad3b7104eea8f334d1830ed5544f42

See more details on using hashes here.

File details

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

File metadata

  • Download URL: codot-0.1.2-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.2-py3-none-any.whl
Algorithm Hash digest
SHA256 fd68c125978740b69c62c80f824a2fac67c5a87e82d5e0d1e9acd94ef6c9d91f
MD5 7e8df2927aadea5730f97a95086ec2b7
BLAKE2b-256 85dcb8e761ea8f9691fb0a1ea4dce55cc87ba0ebd5e82829ec903d16106e86c5

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