Short: server implementation of the Open Share protocol — registration, CRL, trust root and light management plane. Implements OpenAPI + JSON schemas in
openapi/and conformance tests intests/conformance/.
- Overview & goals
- What to implement (product backlog)
- Repo layout & conventions
- Quick start — dev (Docker)
- Deployment — prod notes
- API & schema contract rules
- Secret & key management (Ed25519 server signing key)
- Database & migrations
- Testing & conformance
- Coding standards & static analysis
- Logging, monitoring, error reporting
- Troubleshooting & FAQ
- Appendix: Modular Django settings (drop-in)
This repo implements the canonical Registration Server from the Open Share protocol:
POST /device/register— accepts device pubkey + metadata, issues a signed device certificate (signed with the server Ed25519 signing key).GET /crl— returns the latest signed CRL (ETag/If-None-Match supported).GET /trustroot— returns server trust roots and supported protocol versions.POST /device/revoke— revoke device and increment CRL version.GET /account/devices— list devices for an account.POST /account/register— optional account creation (bootstrap).- Add admin UI / management commands for key rotation, CRL issuance and audit.
Goals:
- Contract-first: API defined by
openapi/openapi.yaml& JSON schemas inopenapi/components/schemas. - Strong crypto: Ed25519 signing, short-lived certs, CRL with versioning.
- Testable: conformance test harness under
tests/conformance/.
Make these endpoints match the OpenAPI in the other repo exactly (contracts + examples):
-
POST /account/register— create account; returnsaccount_id+api_token -
POST /device/register— authenticated byBearer api_token; accepts{ device_id, pubkey_ed25519, metadata }-> returns device certificate JSON matchingdevice-cert.schema.json. Must:- Validate JSON schema
- Verify
device_iduniqueness (per account) - Store
pubkey(raw bytes) in DB - Issue certificate with server signature (Ed25519)
- Store certificate in
device_certs+ set device statusactive - Record audit log entry
-
GET /account/devices— returns device certs (public fields only) -
POST /device/revoke— mark device revoked, append CRL entry, produce new signed CRL (crls), publish viaGET /crl -
GET /crl— returns latestcrl_blobandETag(use CRLversionas ETag) -
GET /trustroot— returns server public key(s) andmin_protocol_version/max_protocol_version
Extra / nice-to-have:
POST /device/renew— rotate cert for a device- Admin endpoints to rotate trustroot (with overlapping roots accepted)
- Web UI for admin operations
- Rate-limits + throttling on registration to prevent abuse
.
├── docker/...
├── docker-compose.yml
├── Dockerfile
├── .env.example
├── manage.py
├── openshare/ # Django project
│ ├── settings/
│ │ ├── base.py
│ │ ├── development.py
│ │ └── production.py
│ ├── urls.py
│ └── wsgi.py
├── apps/
│ ├── accounts/
│ ├── devices/
│ ├── api/ # DRF views/routers/spectacular
│ └── conformance/ # conformance helpers
├── openapi/ # canonical OpenAPI.yaml + JSON schemas (copy from protocol repo)
├── tests/
│ ├── unit/
│ └── integration/
└── README.md
Apps:
accounts— models for account, auth tokens, registrationdevices— device model, cert issuance, device_certs, crl logicapi— DRF viewsets/serializers wired to OpenAPIconformance— test helpers to run the other repo's conformance vectors
- Copy
.env.example->.envand fill values (DB, REDIS, signing keys if you have them). - Start with Docker compose (the compose file from your other repo):
docker compose up --build- Run local conformance tests:
# inside docker or locally with the venv that has jsonschema/pynacl
python3 -m pip install -r tests/conformance/requirements.txt
chmod +x tests/conformance/runner/run_local_tests.sh
tests/conformance/runner/run_local_tests.sh- Access admin UI at
http://localhost:8000/admin(create superuser withmanage.py createsuperuser).
- All authoritative PKI data (trust root, CRL) are persisted in DB
crlstable. - Device certs are canonical JSON signed with server Ed25519 key. Server stores only signature binary and canonical cert JSON (not device private keys).
- Manifest + chunk logic: server stores manifests in
file_manifestsand references chunks by id, but does not serve file contents — file transfers are P2P LAN-first as per spec. - Use
current_crltable or a cache to compute ETag quickly.
- Always validate incoming JSON against the JSON Schema in
openapi/components/schemas/*— usejsonschemaor library that supports draft-2020-12. - When producing certs or CRL, sign canonicalized JSON using the deterministic encoding used in conformance tests (sorted keys, compact separators) — document canonicalization clearly.
- Responses must match the OpenAPI examples: field names, formats (RFC3339 datetimes), and base64url for keys/signatures.
- Use
ETagheaders forGET /crl(value = CRL version or hash). - Use HTTPS in production and
securitySchemesas defined in OpenAPI (bearer tokens). Mutual TLS may be an opt-in for admin endpoints.
Server signing key (Ed25519):
-
Do not store in DB in plaintext. Prefer HSM / Vault. If you must store it on disk (for dev), protect with filesystem perms and keep out of VCS.
-
Recommended pattern:
- Use Vault or KMS to fetch private key at boot time.
- Expose only the public key via
GET /trustroot. - Support key rotation: keep old keys for verification until all devices migrate. Implement
trust_rootsas a list withvalid_from/valid_to.
-
For development only, generate key by
pynacland keep in.envorsecrets/with strict perms.
Suggested env vars:
SIGNING_KEY_TYPE=ed25519SIGNING_KEY_PATH=/run/secrets/signing_keyorSIGNING_KEY_BASE64=...VAULT_ADDR,VAULT_ROLE_ID, etc. if using Vault
Signing helpers:
- Put signing code in
apps.devices.crypto— thin wrapper aroundpynacl(or libsodium via C lib). - Always sign canonicalized bytes; keep signing logic isolated and heavily unit-tested.
- Use the provided PostgreSQL schema as the canonical mapping.
- Django models should match schema columns and types; use
BinaryFieldfor raw public keys/signatures ormodels.BinaryFieldwitheditable=False. - Use manual migrations for any DB features not directly supported by Django ORM (e.g.,
CREATE TYPE device_statusorpgcryptoextension). Add SQL migrations usingRunSQL. - Keep
device_certsandcrlsas audit-capable objects in DB; never silently overwrite. - Use
django-db-connection-pool/ pgBouncer in production.
-
Use
pytest-django+factory_boyfor tests. -
Keep conformance tests in
tests/conformance/calling into the other repo's test vectors. Provide an integration test that fetches generated vectors and validates server behavior. -
Unit tests:
- crypto signing & verification
- schema validation of requests and responses
- DB transactions for cert issuance + audit logging + CRL updates
-
Integration tests:
- full register flow
- revocation and CRL propagation
-
Use coverage and require a minimal coverage threshold (e.g., 85%) in CI.
Adopt strict automated linting/formatting:
- Formatting:
black(configurable line length 88) - Linting:
ruff(fast),flake8(optional) - Imports:
isort - Type checking:
mypy(strict mode for all new code, gradual adoption allowed) - Pre-commit hooks:
.pre-commit-config.yamlexists — ensure hooks forblack,ruff,isort,check-yaml,end-of-file-fixer. - Commit messages: Conventional Commits (
feat:,fix:,chore:,docs:); enable a commit-msg hook if desired. - Docstrings: Google or numpy style, use
pydocstyleoptionally.
- Use Django REST Framework (DRF) for API views.
- Use drf-spectacular to auto-generate OpenAPI v3.1 from DRF. Configure it to use the canonical
openapi/openapi.yamlas the source-of-truth (or add a CI check ensuring generated spec is compatible). - Always add
examplesin serializers that referenceopenapi/components/examples. - Add request/response schema validation middleware that ensures responses match
file-manifest.schema.jsonetc. (optional but highly recommended).
DRF settings hint:
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework.authentication.TokenAuthentication",
],
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticated",
],
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination",
"PAGE_SIZE": 50,
}
SPECTACULAR_SETTINGS = {
"TITLE": "Open Share Registration API",
"VERSION": "1.0.0",
"SCHEMA_PATH_PREFIX": r"/api/",
}- Protect
POST /device/registerandPOST /account/registerwith rate limits. Use DRF throttling or a gateway (Cloudflare, Kong). - Optionally add CAPTCHA for human-facing account creation.
-
Structured JSON logs (use
python-json-loggeror custom formatter and also use structlog). -
Include correlation ids: instrument incoming requests, pass correlation id to logs and background tasks.
-
Expose
/healthzand/readyzendpoints:/healthz= basic app up/readyz= DB connectivity and migrations health
Logging example:
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"json": {
"()": "pythonjsonlogger.jsonlogger.JsonFormatter",
"fmt": "%(asctime)s %(levelname)s %(name)s %(message)s",
}
},
"handlers": {
"stdout": {"class": "logging.StreamHandler", "formatter": "json"},
},
"root": {"handlers": ["stdout"], "level": "INFO"},
}- Enforce HTTPS in production.
SECURE_SSL_REDIRECT = True - Set
SESSION_COOKIE_SECURE = True,CSRF_COOKIE_SECURE = True SECURE_HSTS_SECONDS >= 31536000,SECURE_HSTS_INCLUDE_SUBDOMAINS = True(after testing)- Use
Argon2for password hashing:PASSWORD_HASHERSwithdjango.contrib.auth.hashers.Argon2PasswordHasher. - Keep
DEBUG = Falsein prod. - Set
ALLOWED_HOSTSto concrete hosts. - Use
django-cspfor content security policy. - Avoid storing private signing keys in git. Use Vault/KMS.
- Use Celery for any asynchronous tasks (CRL publishing, garbage collecting chunks, sending notifications).
- Configure broker & result backend via env (
CELERY_BROKER_URL,CELERY_RESULT_BACKEND). - Provide a
celery.pyin Django project that imports tasks and usesapp.autodiscover_tasks().
Migration errors: ensure DB extensions (pgcrypto,citext) created via SQL migration.Crypto verification: ensure canonicalization used by server matches conformance runner exactly (sorted keys, no extra spaces).Slow CRL queries: add index oncrl_entries.revoked_device_idand cachecurrent_crl.crl_id.Missing keys in production: ensure secrets are provisioned via your secrets manager and the container fetches them at start.
- Keep
openapi/in sync with DRF-generated OpenAPI; add a CI job that diffs the generated spec to the canonicalopenapi/openapi.yaml. If they differ, fail CI and require reconciliation. - Make signing/verification crypto small, well-tested modules (
apps.devices.crypto) and never reimplement primitives — usepynacl. - Write integration tests for the entire register -> CRL lifecycle.
- Document canonical JSON canonicalization and keep test vectors in
tests/conformance/vectors. - Establish rotation plan & migration steps for trust-root changes.