Skip to content

Development

Prerequisites

  • Python >= 3.12 and uv
  • task (go-task)
  • PostgreSQL at localhost:15432 (credentials postgres:securepassword123)

Local Setup

uv sync
task alembic:migrate       # apply database migrations
task seed                  # seed rules and templates
task run                   # start on port 8016

Deployment posture

task run and task debug export CELINE_ENV=dev, which is what lets the zero-config defaults start. Unset or any other value (staging, prod, …) is hardened (celine.sdk.posture), and create_app() refuses to start on a local database password, a client secret equal to the client id, the SDK's default issuer, or a missing VAPID_PUBLIC_KEY / VAPID_PRIVATE_KEY / CLICK_TRACKING_SECRET (REQ-0081). In dev the same findings are one warning. CELINE_ENV=staging task run runs the same entry point hardened.

celine.sdk.posture ships in celine-sdk 2.0.0, the floor in pyproject.toml.

Taskfile Commands

Command Description
task run Start dev server on port 8016
task debug Start with debugger (port 48016)
task test Run pytest
task test:real-tokens Run the opt-in real-token layer against the local Keycloak
task seed Seed rules from ./seed directory
task alembic:migrate Apply all pending migrations
task alembic:sync-model Generate new Alembic migration
task alembic:reset Reset DB to base
task release Run semantic-release

VAPID Key Generation

nudging-cli vapid

Set the generated keys as VAPID_PUBLIC_KEY and VAPID_PRIVATE_KEY environment variables.

Seed Data Management

Rules are stored in seed/rules/<rule_id>/ directories, each containing: - rule.yaml — rule definition - evaluate.py — custom Python evaluator - templates/<lang>.yaml — one Jinja2 title and body per language. Templates are not per channel: web push and email send the same rendered strings.

The directory name is the rule id, and it is also where the evaluator is looked for — see REQ-0028 and REQ-0068.

Apply seeds via:

task seed
# or directly:
nudging-cli seed apply ./seed --client-id celine-cli --client-secret celine-cli

The CLI calls POST /admin/seed/apply to upsert rules and templates.

Database Migrations

task alembic:migrate                          # apply pending
task alembic:sync-model -- "description"      # create new migration
task alembic:reset                            # reset to base

Running Tests

task test                     # uv run pytest
uv run pytest tests/unit      # the layer without an app

The suite reaches no external service — no PostgreSQL, no Keycloak, no push service, no SMTP — so it needs nothing running. The Rego bundle is the exception and is evaluated for real, in process; see ADR-0002.

tests/integration/ is the one opt-in layer: it mints tokens from the local Keycloak, verifies them against its JWKS and drives the real middleware and policy. It is skipped unless NUDGING_REAL_TOKENS=1 (task test:real-tokens) and refuses a non-local issuer; a token still carrying a retired realm group is passed in as NUDGING_LEGACY_ADMIN_TOKEN. See ADR-0004.

Every test declares the requirement it covers with @verifies REQ-####, and the requirements are in docs/specifications/. Adding behaviour means adding a requirement and a test naming it, in the same change.

The procedure, the fixtures and the traps are in the companion's testing playbook.

Project Layout

src/celine/nudging/
  main.py                         # FastAPI app factory (create_app)
  settings.py                     # Pydantic settings
  scheduler.py                    # Background scheduled event processor
  api/routes/
    meta.py                       # /health
    webpush.py                    # /webpush/* (subscribe, unsubscribe, vapid)
    notifications.py              # /notifications (user-facing)
    preferences.py                # /preferences/me, /preferences/catalog
    admin/
      ingest.py                   # /admin/ingest-event
      webpush.py                  # /admin/webpush/send-test
      notifications.py            # /admin/notifications
      scheduled_events.py         # /admin/scheduled-events
      seed.py                     # /admin/seed/apply
  engine/
    engine_service.py             # Rule evaluation and template rendering
  orchestrator/
    orchestrator.py               # Delivery policy enforcement
    preferences.py                # User preference resolution
  publishers/
    web/worker.py                 # Web push delivery
    email/worker.py               # Email delivery
  db/
    models.py                     # SQLAlchemy ORM models
    session.py                    # Async session management
    auto_seed.py                  # Auto-seed on startup
  cli/
    main.py                       # CLI entry point (nudging-cli)
policies/                         # OPA .rego policy files
seed/                             # Rule definitions and templates
  rules/                          # Per-rule directories
  active_kinds.yaml               # Notification kind catalog
alembic/                          # Database migrations