svc-infra 0.1.622__py3-none-any.whl → 0.1.623__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of svc-infra might be problematic. Click here for more details.

Files changed (33) hide show
  1. svc_infra/cli/cmds/docs/docs_cmds.py +104 -140
  2. svc_infra/docs/acceptance-matrix.md +71 -0
  3. svc_infra/docs/acceptance.md +44 -0
  4. svc_infra/docs/adr/0002-background-jobs-and-scheduling.md +40 -0
  5. svc_infra/docs/adr/0003-webhooks-framework.md +24 -0
  6. svc_infra/docs/adr/0004-tenancy-model.md +42 -0
  7. svc_infra/docs/adr/0005-data-lifecycle.md +86 -0
  8. svc_infra/docs/adr/0006-ops-slos-and-metrics.md +47 -0
  9. svc_infra/docs/adr/0007-docs-and-sdks.md +83 -0
  10. svc_infra/docs/adr/0008-billing-primitives.md +109 -0
  11. svc_infra/docs/adr/0009-acceptance-harness.md +40 -0
  12. svc_infra/docs/api.md +59 -0
  13. svc_infra/docs/auth.md +11 -0
  14. svc_infra/docs/cache.md +18 -0
  15. svc_infra/docs/cli.md +74 -0
  16. svc_infra/docs/contributing.md +34 -0
  17. svc_infra/docs/data-lifecycle.md +52 -0
  18. svc_infra/docs/database.md +14 -0
  19. svc_infra/docs/docs-and-sdks.md +62 -0
  20. svc_infra/docs/environment.md +114 -0
  21. svc_infra/docs/idempotency.md +111 -0
  22. svc_infra/docs/jobs.md +67 -0
  23. svc_infra/docs/observability.md +16 -0
  24. svc_infra/docs/ops.md +33 -0
  25. svc_infra/docs/rate-limiting.md +121 -0
  26. svc_infra/docs/repo-review.md +48 -0
  27. svc_infra/docs/security.md +155 -0
  28. svc_infra/docs/tenancy.md +35 -0
  29. svc_infra/docs/webhooks.md +112 -0
  30. {svc_infra-0.1.622.dist-info → svc_infra-0.1.623.dist-info}/METADATA +16 -16
  31. {svc_infra-0.1.622.dist-info → svc_infra-0.1.623.dist-info}/RECORD +33 -5
  32. {svc_infra-0.1.622.dist-info → svc_infra-0.1.623.dist-info}/WHEEL +0 -0
  33. {svc_infra-0.1.622.dist-info → svc_infra-0.1.623.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,47 @@
1
+ # ADR-0006: Ops SLOs, SLIs, and Metrics Naming
2
+
3
+ Date: 2025-10-16
4
+
5
+ ## Status
6
+ Accepted
7
+
8
+ ## Context
9
+ We already expose Prometheus metrics via `svc_infra.obs.add.add_observability`, which mounts the `PrometheusMiddleware` and exports:
10
+ - `http_server_requests_total{method,route,code}`
11
+ - `http_server_request_duration_seconds_bucket{route,method}` + _sum/_count
12
+ - `http_server_inflight_requests{route}`
13
+ - `http_server_response_size_bytes_bucket` + _sum/_count (where available)
14
+ - `http_server_exceptions_total{route,exception}` (where available)
15
+
16
+ We also optionally expose SQLAlchemy pool metrics and instrument `requests`/`httpx`. Logging is configured via `svc_infra.app.logging.setup_logging`.
17
+
18
+ ## Decision
19
+ 1. Metric naming and labels
20
+ - Keep `http_server_*` naming aligned with Prometheus and OpenTelemetry conventions.
21
+ - Labels: `route` uses normalized FastAPI route pattern (e.g., `/users/{id}`); `method` is uppercase HTTP verb; `code` is the 3-digit status.
22
+ - Add DB pool metrics with `db_pool_*` prefix when bound (labels: `engine`/`pool_name`).
23
+ 2. SLIs
24
+ - Request Success Rate: 1 - error_ratio, where errors are 5xx by default; optionally include 429/499 as errors per service config.
25
+ - Request Latency: p50/p90/p99 on `http_server_request_duration_seconds` by `route` and overall.
26
+ - Availability (Probes): uptime of `/_ops/live` and `/_ops/ready` endpoints.
27
+ 3. SLOs
28
+ - Default SLOs per service class:
29
+ - Public API: 99.9% success, p99 < 500ms.
30
+ - Internal API/Jobs control plane: 99.5% success, p99 < 1000ms.
31
+ - Error Budget: monthly window; alert on burn rates of 2h (fast) and 24h (slow). Budgets computed from success SLI.
32
+ 4. Dashboards & Alerts
33
+ - Provide Grafana JSON dashboard templates referencing the above metrics and labels.
34
+ - Include alert rules for budget burn (fast/slow).
35
+
36
+ ## Consequences
37
+ - Developers can rely on consistent metrics and labels for dashboards.
38
+ - SLO targets are explicit and can be overridden per service.
39
+ - Future work: Emit `http_server_exceptions_total` where missing; provide helper to register per-route classes (public/internal/admin) to pick default SLOs.
40
+
41
+ ## Alternatives Considered
42
+ - OpenTelemetry SDK direct instrumentation was considered but deferred to keep dependency surface minimal; we keep the naming aligned for easy migration.
43
+
44
+ ## References
45
+ - `src/svc_infra/obs/metrics/asgi.py`
46
+ - `src/svc_infra/api/fastapi/ops/add.py`
47
+ - Google SRE Workbook: SLOs and Error Budgets
@@ -0,0 +1,83 @@
1
+ # ADR 0007: Docs & SDKs — Research and Design
2
+
3
+ Status: Proposed
4
+
5
+ Date: 2025-10-16
6
+
7
+ ## Context
8
+
9
+ We want a production-ready documentation and SDK experience built on our existing FastAPI scaffolding.
10
+ Current capabilities in the codebase:
11
+
12
+ - Docs endpoints and export
13
+ - `add_docs(app, redoc_url, swagger_url, openapi_url, export_openapi_to)` mounts Swagger, ReDoc, and OpenAPI JSON; optional export on startup.
14
+ - `setup_service_api(...)` renders a landing page with per-version doc cards and local-only root docs.
15
+ - `add_prefixed_docs(...)` exposes scoped docs (e.g., for auth/payments) with per-scope OpenAPI, Swagger, ReDoc.
16
+ - OpenAPI conventions and enrichment pipeline
17
+ - Mutators pipeline (`openapi/mutators.py`) with: conventions, normalized Problem schema, pagination params/components, header params, info mutator, and auth scheme installers.
18
+ - Conventions define `Problem` schema and normalize examples.
19
+ - DX checks
20
+ - OpenAPI Problem+JSON lint in `dx/checks.py` and CLI to validate.
21
+ - SDK stub
22
+ - `add_sdk_generation_stub(app, on_generate=...)` exposes a hook endpoint to trigger SDK generation (no hard deps).
23
+
24
+ Gaps for a complete v1 experience:
25
+
26
+ - Enriched OpenAPI with examples and tags is not yet standardized across routers.
27
+ - No built-in SDK generator CLI; only a stub exists. No pinned toolchain or CI integration.
28
+ - No Postman collection generator.
29
+ - No dark-mode toggle/themes for Swagger/ReDoc (landing page supports light/dark).
30
+ - No smoke tests for generated SDKs.
31
+
32
+ ## Decision
33
+
34
+ We will standardize the Docs & SDKs approach around the following:
35
+
36
+ 1) OpenAPI enrichment
37
+ - Use existing mutators pipeline and add small mutators to:
38
+ - Inject global tags and tag descriptions for major areas (auth, payments, webhooks, ops).
39
+ - Attach minimal `x-codeSamples` for common operations (curl/httpie).
40
+ - Ensure `Problem` schema and example responses are present across 4xx/5xx.
41
+ - Keep pagination and header parameter mutators enabled by default.
42
+
43
+ 2) Docs UI
44
+ - Continue with Swagger UI and ReDoc via `add_docs` and `setup_service_api`.
45
+ - Add an optional dark mode toggle for Swagger UI via custom CSS and a query param (design-only; implement later).
46
+ - Keep local-only exposure of root docs; version-specific docs always exposed under their mount path.
47
+
48
+ 3) SDK generation pipeline (tools and layout)
49
+ - TypeScript: `openapi-typescript` to generate types (no runtime client) to `clients/typescript/`.
50
+ - Python: `openapi-python-client` to generate a client package to `clients/python/`.
51
+ - Provide a new CLI group `svc-infra sdk` with subcommands:
52
+ - `svc-infra sdk ts --schema openapi.json --out clients/typescript --package @org/service`
53
+ - `svc-infra sdk py --schema openapi.json --out clients/python --package service_sdk`
54
+ - `svc-infra sdk postman --schema openapi.json --out clients/postman_collection.json` (via converter)
55
+ - Pin generator versions in a minimal tool manifest (poetry extras and npm devDeps suggestions in docs) rather than hard deps in core library.
56
+ - Add optional CI steps to generate SDKs on release tags; artifacts uploaded; publishing pipelines documented.
57
+
58
+ 4) Postman collection
59
+ - Use the Postman converter (`openapi-to-postmanv2`) to produce `clients/postman_collection.json` from the exported OpenAPI.
60
+
61
+ 5) Testing & verification
62
+ - Extend `dx` checks to include: schema export presence, generator dry-run, and minimal smoke tests:
63
+ - TS: typecheck the generated d.ts.
64
+ - Python: `pip install -e` and import a sample client in a quick script.
65
+ - Keep these checks optional (opt-in via CI config) to avoid burdening minimal users.
66
+
67
+ ## Consequences
68
+
69
+ - Pros: Clear, tool-agnostic pipeline; no heavy runtime dependencies; easy local and CI usage; versioned artifacts.
70
+ - Cons: Adds extra tooling expectations (node and python generators) for teams that opt in.
71
+ - Risk: Generator/tooling churn; mitigate by pinning versions and providing stubs/fallbacks.
72
+
73
+ ## Implementation Notes (planned)
74
+
75
+ - Provide a small `svc_infra/cli/cmds/sdk` module with Typer commands that shell out to the generators if available, with helpful error messages if missing.
76
+ - Document usage in `docs/docs-and-sdks.md` (to be added), including examples and troubleshooting.
77
+ - Keep all new code behind DX/CLI; core library remains free of generator dependencies.
78
+
79
+ ## Out of Scope (v1)
80
+
81
+ - Live “try it” consoles beyond Swagger UI.
82
+ - Multi-language example snippets beyond curl/httpie.
83
+ - Automatic publishing to npm/PyPI (documented manual workflows first).
@@ -0,0 +1,109 @@
1
+ # ADR 0008: Billing Primitives (Usage, Quotas, Invoicing)
2
+
3
+ ## Status
4
+
5
+ Proposed — Research and Design complete for v1 scope.
6
+
7
+ ## Context
8
+
9
+ We need shared billing primitives to support both usage-based and subscription features across services. Goals:
10
+ - Capture fine-grained usage events with idempotency and tenant isolation.
11
+ - Aggregate usage into billable buckets (hour/day/month) with rollups.
12
+ - Enforce entitlements/quotas at runtime (hard/soft limits).
13
+ - Produce invoice data structures and events; enable later integration with external providers (Stripe, Paddle) without coupling core DX to any vendor.
14
+
15
+ Non-goals for v1: taxes/VAT, complex proration rules, refunds/credits automation, dunning flows, provider-specific webhooks/end-to-end reconciliation.
16
+
17
+ ## Decisions
18
+
19
+ 1) Internal-first data model with optional provider adapters
20
+ - Persist usage, aggregates, plans, subscriptions, invoices in our SQL layer.
21
+ - Provide interfaces for provider adapters (Stripe later) to map internal invoices/lines and sync state when enabled.
22
+
23
+ 2) Usage ingestion API + idempotency
24
+ - FastAPI router exposes POST /_billing/usage capturing events: {tenant_id, metric, amount, at, idempotency_key, metadata}.
25
+ - Enforce request idempotency via existing middleware + usage-event unique index on (tenant_id, metric, idempotency_key).
26
+ - Emit webhook event `billing.usage_recorded` (optional).
27
+
28
+ 3) Aggregation job (scheduler)
29
+ - Background job reads new UsageEvent rows, aggregates into UsageAggregate by key (tenant, metric, period_start, period_granularity).
30
+ - Granularities: hour, day, month (config). Maintains running totals; idempotent.
31
+ - Emits `billing.usage_aggregated` webhook.
32
+
33
+ 4) Entitlements and quotas
34
+ - Define Plan and PlanEntitlement models (feature flags, quotas per window).
35
+ - Subscriptions bind tenant -> plan, effective_at/ended_at.
36
+ - Runtime enforcement via dependency/decorator: `require_quota("metric", window="day", soft=True)` which raises/records when limit exceeded.
37
+
38
+ 5) Invoicing primitives
39
+ - Invoice and InvoiceLine models created for each billing cycle (monthly default). Lines derived from aggregates and static prices.
40
+ - Price model: unit amount, currency, metric reference (for metered), or fixed recurring.
41
+ - Emit `billing.invoice_created` and `billing.invoice_finalized` webhooks; provider adapter can consume and sync out.
42
+
43
+ 6) Observability
44
+ - Metrics: `billing_usage_ingest_total`, `billing_aggregate_duration_ms`, `billing_invoice_generated_total`.
45
+ - Logs: aggregation windows processed, invoice cycles.
46
+
47
+ 7) Security & tenancy
48
+ - All models include tenant_id; APIs require tenant context. RBAC: billing.read/billing.write for admin/operator roles.
49
+
50
+ ## Data Model (SQL)
51
+
52
+ Tables (minimal v1):
53
+ - usage_events(id, tenant_id, metric, amount, at_ts, idempotency_key, metadata_json, created_at)
54
+ - Unique (tenant_id, metric, idempotency_key)
55
+ - usage_aggregates(id, tenant_id, metric, period_start, granularity, total, updated_at)
56
+ - Unique (tenant_id, metric, period_start, granularity)
57
+ - plans(id, key, name, description, created_at)
58
+ - plan_entitlements(id, plan_id, key, limit_per_window, window, created_at)
59
+ - subscriptions(id, tenant_id, plan_id, effective_at, ended_at, created_at)
60
+ - prices(id, key, currency, unit_amount, metric, recurring_interval, created_at)
61
+ - invoices(id, tenant_id, period_start, period_end, status, total_amount, currency, created_at)
62
+ - invoice_lines(id, invoice_id, price_id, metric, quantity, amount, created_at)
63
+
64
+ All tables will be scaffolded with our SQL helpers and tenant mixin, with Alembic templates.
65
+
66
+ ## APIs
67
+
68
+ - POST /_billing/usage: record usage events (body as above). Returns 202 with event id.
69
+ - GET /_billing/usage: list usage by metric and window (aggregated).
70
+ - GET /_billing/plans, GET /_billing/subscriptions, POST /_billing/subscriptions.
71
+ - GET /_billing/invoices, GET /_billing/invoices/{id}.
72
+
73
+ Routers mounted under a `/_billing` prefix and hidden behind auth + tenant guard. OpenAPI tags: Billing.
74
+
75
+ ## Jobs & Webhooks
76
+
77
+ - Job: `aggregate_usage` runs on schedule; creates/updates UsageAggregate rows.
78
+ - Job: `generate_invoices` runs monthly; emits invoice events and inserts Invoice/InvoiceLine rows.
79
+ - Webhooks: `billing.usage_recorded`, `billing.usage_aggregated`, `billing.invoice_created`, `billing.invoice_finalized` (signed via existing module).
80
+
81
+ ## Implementation Plan (Phased)
82
+
83
+ Phase 1 (MVP):
84
+ - Models + migrations; CRUD for Plans/Subs/Prices; Usage ingestion + idempotency; Aggregator job (daily granularity); Basic invoice generator (monthly, fixed price + metered by day sum); Webhooks emitted; Tests for ingestion, aggregation, simple invoice.
85
+
86
+ Phase 2:
87
+ - Granularity options (hourly); soft/hard quota decorator; Read APIs; Observability metrics; Docs.
88
+
89
+ Phase 3 (Provider adapter optional):
90
+ - Stripe adapter skeleton: map internal invoices/lines -> Stripe, idempotent sync; basic webhook handler to update statuses.
91
+
92
+ ## Alternatives Considered
93
+
94
+ - Provider-first approach (Stripe-only) rejected for v1 to keep core DX portable and support non-card use-cases.
95
+ - Event-stream aggregation (Kafka) out-of-scope for framework baseline—can be integrated later.
96
+
97
+ ## Risks
98
+
99
+ - Complexity creep around proration and taxes—explicitly out-of-scope for v1.
100
+ - Performance on large tenants—mitigated by granular aggregation and indexes.
101
+
102
+ ## Testing
103
+
104
+ - Unit tests for ingestion idempotency, aggregation correctness, invoice totals.
105
+ - E2E-ish tests using in-memory queue + sqlite.
106
+
107
+ ## Documentation
108
+
109
+ - `docs/billing.md`: usage API, quotas, invoice lifecycle, and Stripe adapter notes.
@@ -0,0 +1,40 @@
1
+ # ADR 0009: Acceptance Harness & Promotion Gate (A0)
2
+
3
+ Date: 2025-10-17
4
+ Status: Proposed
5
+ Decision: Adopt a post-build acceptance harness that brings up an ephemeral stack (Docker Compose) and gates image promotion on acceptance results.
6
+
7
+ ## Context
8
+ - We need a thin but strict pre-deploy acceptance layer that runs after building images, before promotion.
9
+ - It should validate golden paths across domains and basic operational invariants.
10
+ - It must be easy to run locally and in CI and support a backend matrix (in-memory vs Redis+Postgres).
11
+ - Supply-chain checks (SBOM, image scan, provenance) should be part of the gate.
12
+
13
+ ## Decision
14
+ - Introduce A0 Acceptance Harness:
15
+ - Compose stack (api + db + redis), Makefile helpers (accept/up/wait/seed/down).
16
+ - Seed CLI/script to create ADMIN/USER/TENANT fixtures and API key.
17
+ - Acceptance tests under `tests/acceptance` with `@pytest.mark.acceptance` and BASE_URL.
18
+ - CI job `build-and-accept` steps: build → compose up → seed → `pytest -m "acceptance or smoke"` → OpenAPI lint + API Doctor → teardown.
19
+ - Supply-chain: generate SBOM, image scan (Trivy/Grype) with severity threshold; upload SBOM.
20
+ - Provenance: sign/attest images via cosign/SLSA (best-effort for v1).
21
+ - Backend matrix: two jobs (in-memory vs Redis+Postgres).
22
+
23
+ ## Alternatives
24
+ - Testcontainers-only approach (simpler per-test spin-up) — good DX but slower; we can adopt later for certain suites.
25
+ - Kubernetes-in-Docker (kind) for near-prod parity — heavier; likely a v2 improvement.
26
+
27
+ ## Consequences
28
+ - Slightly longer CI time due to matrix and scans.
29
+ - Clearer promotion safety; early detection of config/env gaps.
30
+
31
+ ## Implementation Notes
32
+ - Files to add:
33
+ - `docker-compose.test.yml`
34
+ - `Makefile` targets: `accept`, `compose_up`, `wait`, `seed`, `down`
35
+ - `tests/acceptance/` scaffolding: `conftest.py`, `_seed.py`, `_auth.py`, `_http.py`, first tests (headers/CORS)
36
+ - CI: `.github/workflows/ci.yml` job `build-and-accept`
37
+ - Env contracts:
38
+ - `SQL_URL`, `REDIS_URL` for backend matrix; `APP_ENV=test-accept` for toggles.
39
+ - Evidence:
40
+ - CI run URL, SBOM artifact link, scan report, acceptance summary.
svc_infra/docs/api.md ADDED
@@ -0,0 +1,59 @@
1
+ # FastAPI helper guide
2
+
3
+ The `svc_infra.api.fastapi` package provides a one-call bootstrap (`easy_service_app`) that wires request IDs, idempotency, rate limiting, and shared docs defaults for every mounted version. 【F:src/svc_infra/api/fastapi/ease.py†L176-L220】【F:src/svc_infra/api/fastapi/setup.py†L55-L129】
4
+
5
+ ```python
6
+ from svc_infra.api.fastapi.ease import easy_service_app
7
+
8
+ app = easy_service_app(
9
+ name="Payments",
10
+ release="1.0.0",
11
+ versions=[("v1", "myapp.api.v1", None)],
12
+ public_cors_origins=["https://app.example.com"],
13
+ )
14
+ ```
15
+
16
+ ### Environment
17
+
18
+ `easy_service_app` merges explicit flags with `EasyAppOptions.from_env()` so you can flip behavior without code changes:
19
+
20
+ - `ENABLE_LOGGING`, `LOG_LEVEL`, `LOG_FORMAT` – control structured logging defaults. 【F:src/svc_infra/api/fastapi/ease.py†L67-L104】
21
+ - `ENABLE_OBS`, `METRICS_PATH`, `OBS_SKIP_PATHS` – opt into Prometheus/OTEL middleware and tweak metrics exposure. 【F:src/svc_infra/api/fastapi/ease.py†L67-L111】
22
+ - `CORS_ALLOW_ORIGINS` – add allow-listed origins when you don’t pass `public_cors_origins`. 【F:src/svc_infra/api/fastapi/setup.py†L47-L88】
23
+
24
+ ## Quickstart
25
+
26
+ Use `easy_service_app` for a batteries-included FastAPI with sensible defaults:
27
+
28
+ Inputs
29
+ - name: service display name used in docs and logs
30
+ - release: version string (shown in docs and headers)
31
+ - versions: list of tuples of (prefix, import_path, router_name_or_None)
32
+ - public_cors_origins: list of allowed origins for CORS (default deny if omitted)
33
+
34
+ Defaults
35
+ - Logging: enabled with JSON or plain format based on `LOG_FORMAT`; level from `LOG_LEVEL`
36
+ - Observability: Prometheus metrics and OTEL when `ENABLE_OBS=true`; metrics path from `METRICS_PATH` (default `/metrics`)
37
+ - Security headers: strict defaults; CORS disabled unless allowlist provided or `CORS_ALLOW_ORIGINS` set
38
+ - Health: `/ping`, `/healthz`, `/readyz`, `/startupz` are wired
39
+
40
+ Example
41
+ ```python
42
+ from svc_infra.api.fastapi.ease import easy_service_app
43
+
44
+ app = easy_service_app(
45
+ name="Example API",
46
+ release="1.0.0",
47
+ versions=[("v1", "example.api.v1", None)],
48
+ public_cors_origins=["https://app.example.com"],
49
+ )
50
+ ```
51
+
52
+ Override with environment
53
+ ```bash
54
+ export ENABLE_LOGGING=true
55
+ export LOG_LEVEL=INFO
56
+ export ENABLE_OBS=true
57
+ export METRICS_PATH=/metrics
58
+ export CORS_ALLOW_ORIGINS=https://app.example.com,https://admin.example.com
59
+ ```
svc_infra/docs/auth.md ADDED
@@ -0,0 +1,11 @@
1
+ # Auth settings
2
+
3
+ `svc_infra.api.fastapi.auth` wraps FastAPI Users with sensible defaults for sessions, OAuth, MFA, and API keys via `add_auth_users`. Configuration comes from `AuthSettings`, which reads environment variables with the `AUTH_` prefix. 【F:src/svc_infra/api/fastapi/auth/add.py†L240-L321】【F:src/svc_infra/api/fastapi/auth/settings.py†L23-L91】
4
+
5
+ ### Key environment variables
6
+
7
+ - `AUTH_JWT__SECRET`, `AUTH_JWT__OLD_SECRETS` – rotate signing keys without downtime. 【F:docs/security.md†L63-L70】
8
+ - `AUTH_SMTP_HOST`, `AUTH_SMTP_USERNAME`, `AUTH_SMTP_PASSWORD`, `AUTH_SMTP_FROM` – enable SMTP delivery; required in production. 【F:src/svc_infra/api/fastapi/auth/settings.py†L44-L60】【F:src/svc_infra/api/fastapi/auth/sender.py†L33-L59】
9
+ - `AUTH_SESSION_COOKIE_SECURE`, `AUTH_SESSION_COOKIE_NAME`, `AUTH_SESSION_COOKIE_SAMESITE` – shape session middleware. 【F:src/svc_infra/api/fastapi/auth/settings.py†L65-L88】【F:src/svc_infra/api/fastapi/auth/add.py†L279-L303】
10
+ - `AUTH_PASSWORD_MIN_LENGTH`, `AUTH_PASSWORD_REQUIRE_SYMBOL`, `AUTH_PASSWORD_BREACH_CHECK` – enforce password policy. 【F:docs/security.md†L24-L35】
11
+ - `AUTH_MFA_DEFAULT_ENABLED_FOR_NEW_USERS`, `AUTH_MFA_ENFORCE_FOR_ALL_USERS` – adjust MFA enforcement. 【F:src/svc_infra/api/fastapi/auth/settings.py†L32-L40】
@@ -0,0 +1,18 @@
1
+ # Cache guide
2
+
3
+ The cache module wraps [cashews](https://github.com/Krukov/cashews) with decorators and namespace helpers so services can centralize key formats.
4
+
5
+ ```python
6
+ from svc_infra.cache import cache_read, cache_write, init_cache
7
+
8
+ init_cache() # uses CACHE_PREFIX / CACHE_VERSION
9
+
10
+ @cache_read(key="user:{user_id}", ttl=300)
11
+ async def get_user(user_id: int):
12
+ ...
13
+ ```
14
+
15
+ ### Environment
16
+
17
+ - `CACHE_PREFIX`, `CACHE_VERSION` – change the namespace alias used by the decorators. 【F:src/svc_infra/cache/README.md†L20-L173】
18
+ - `CACHE_TTL_DEFAULT`, `CACHE_TTL_SHORT`, `CACHE_TTL_LONG` – override canonical TTL buckets. 【F:src/svc_infra/cache/ttl.py†L26-L55】
svc_infra/docs/cli.md ADDED
@@ -0,0 +1,74 @@
1
+ # CLI Quick Reference
2
+
3
+ The `svc-infra` CLI wraps common database, observability, jobs, docs, and DX workflows using Typer.
4
+
5
+ - Entry points:
6
+ - Global: `svc-infra ...` (installed via Poetry scripts)
7
+ - Module: `python -m svc_infra.cli ...` (works in editable installs and containers)
8
+
9
+ ## Top-level help
10
+
11
+ Run:
12
+
13
+ ```
14
+ svc-infra --help
15
+ ```
16
+
17
+ You should see groups for SQL, Mongo, Observability, DX, Jobs, and SDK.
18
+
19
+ ## Database (Alembic) commands
20
+
21
+ - End-to-end setup and migrate (detects async from URL):
22
+ - Environment variables (commonly): `SQL_URL` or compose parts `DB_*`.
23
+
24
+ Example with SQLite for quick smoke tests:
25
+
26
+ ```
27
+ python -m svc_infra.cli sql setup-and-migrate --database-url sqlite+aiosqlite:///./accept.db \
28
+ --discover-packages "app.models" --with-payments false
29
+ ```
30
+
31
+ - Current revision, history, upgrade/downgrade:
32
+
33
+ ```
34
+ python -m svc_infra.cli sql current
35
+ python -m svc_infra.cli sql-history
36
+ python -m svc_infra.cli sql upgrade head
37
+ python -m svc_infra.cli sql downgrade -1
38
+ ```
39
+
40
+ - Seed fixtures/reference data with your callable:
41
+
42
+ ```
43
+ python -m svc_infra.cli sql seed path.to.module:seed_func
44
+ ```
45
+
46
+ Notes:
47
+ - The target must be in the format `module.path:callable`.
48
+ - If you previously referenced legacy test modules under `tests.db.*`, the CLI shims import to `tests.unit.db.*` when possible.
49
+
50
+ ## Jobs
51
+
52
+ Start the local jobs runner loop:
53
+
54
+ ```
55
+ svc-infra jobs run
56
+ ```
57
+
58
+ ## DX helpers
59
+
60
+ - Generate CI workflow and checks template:
61
+
62
+ ```
63
+ python -m svc_infra.cli dx ci --openapi openapi.json
64
+ ```
65
+
66
+ - Lint OpenAPI and Problem+JSON samples:
67
+
68
+ ```
69
+ python -m svc_infra.cli dx openapi openapi.json
70
+ ```
71
+
72
+ ## SDKs
73
+
74
+ Generate SDKs from OpenAPI (dry-run by default): see `docs/docs-and-sdks.md` for full examples.
@@ -0,0 +1,34 @@
1
+ # Contributing
2
+
3
+ Thanks for considering a contribution! This repo aims to provide production-ready primitives with clear gates.
4
+
5
+ ## Local setup
6
+
7
+ - Python 3.11–3.13
8
+ - Install via Poetry:
9
+ - `poetry install`
10
+ - `poetry run pre-commit install`
11
+
12
+ ## Quality gates (run before PR)
13
+
14
+ - Lint: `poetry run flake8 --select=E,F`
15
+ - Typecheck: `poetry run mypy src`
16
+ - Tests: `poetry run pytest -q -W error`
17
+ - OpenAPI lint (optional): `poetry run python -m svc_infra.cli dx openapi openapi.json`
18
+ - Migrations present (optional): `poetry run python -m svc_infra.cli dx migrations --project-root .`
19
+ - CI dry-run (optional): `poetry run python -m svc_infra.cli dx ci --openapi openapi.json`
20
+
21
+ ## Commit style
22
+
23
+ - Prefer Conventional Commits: `feat:`, `fix:`, `refactor:`, etc.
24
+ - Use changelog generator for releases:
25
+ - `poetry run python -m svc_infra.cli dx changelog 0.1.604 --commits-file commits.jsonl`
26
+
27
+ ## Release process
28
+
29
+ 1. Ensure all gates are green locally (see above).
30
+ 2. Update version in `pyproject.toml`.
31
+ 3. Export OpenAPI (if applicable) via docs helper.
32
+ 4. Generate changelog section and review.
33
+ 5. Merge to `main`. CI will run tests, lint, and typecheck.
34
+ 6. Tag and publish.
@@ -0,0 +1,52 @@
1
+ # Data Lifecycle
2
+
3
+ This guide covers fixtures (reference data), retention policies (soft/hard delete), GDPR erasure, and backup verification.
4
+
5
+ ## Quickstart
6
+
7
+ - Fixtures:
8
+ - Use `run_fixtures([...])` for ad-hoc loads.
9
+ - Or wire a one-time loader with `make_on_load_fixtures(fn, run_once_file)`, then `add_data_lifecycle(app, on_startup=[on_load])`.
10
+ - Retention:
11
+ - Define `RetentionPolicy(name, model, older_than_days, soft_delete_field|None, hard_delete=False)`.
12
+ - Execute manually with `await run_retention_purge(session, [policy,...])` or schedule via your jobs runner.
13
+ - Erasure:
14
+ - Compose an `ErasurePlan([ErasureStep(name, func), ...])` where functions accept `(session, principal_id)` and may be async.
15
+ - Run with `await run_erasure(session, principal_id, plan, on_audit=callable)`; `on_audit` receives `(event, context)`.
16
+ - Backups:
17
+ - `verify_backups(last_success: datetime|None, retention_days: int)` returns a `BackupHealthReport`.
18
+ - Wrap as a job: `make_backup_verification_job(checker, on_report=callback)`.
19
+
20
+ ## APIs
21
+
22
+ - `fixtures.py`:
23
+ - `run_fixtures(callables: Iterable[Callable]) -> Awaitable[None]`
24
+ - `make_on_load_fixtures(*fns, run_once_file: str | None = None) -> Callable[[], Awaitable[None]]`
25
+ - `retention.py`:
26
+ - `RetentionPolicy(name, model, older_than_days, soft_delete_field: str | None, hard_delete: bool = False)`
27
+ - `run_retention_purge(session, policies: Sequence[RetentionPolicy]) -> Awaitable[int]`
28
+ - `erasure.py`:
29
+ - `ErasureStep(name: str, func: Callable)`
30
+ - `ErasurePlan(steps: Sequence[ErasureStep])`
31
+ - `run_erasure(session, principal_id: str, plan: ErasurePlan, on_audit: Callable | None = None) -> Awaitable[int]`
32
+ - `backup.py`:
33
+ - `BackupHealthReport(ok: bool, last_success: datetime | None, reason: str | None)`
34
+ - `verify_backups(last_success: datetime | None = None, retention_days: int = 1) -> BackupHealthReport`
35
+ - `make_backup_verification_job(checker: Callable[[], BackupHealthReport], on_report: Callable[[BackupHealthReport], None] | None = None) -> Callable[[], BackupHealthReport]`
36
+
37
+ ## Scheduling
38
+
39
+ Use the jobs helpers to run retention and backup checks periodically. Example schedule JSON (via JOBS_SCHEDULE_JSON):
40
+
41
+ ```json
42
+ [
43
+ {"name": "retention-purge", "interval": "6h", "handler": "your.module:run_retention"},
44
+ {"name": "backup-verify", "interval": "12h", "handler": "your.module:verify_backups_job"}
45
+ ]
46
+ ```
47
+
48
+ ## Notes
49
+
50
+ - Soft delete expects a `deleted_at` column and optionally an `is_active` flag in repositories.
51
+ - `run_fixtures` and erasure steps support async functions seamlessly.
52
+ - `add_data_lifecycle` already awaits async fixture loaders and uses lifespan instead of deprecated startup events.
@@ -0,0 +1,14 @@
1
+ # Database guide
2
+
3
+ svc-infra exposes helpers for SQLAlchemy and Mongo so APIs get lifecycle management, migrations, and connection URLs from environment variables.
4
+
5
+ ## SQL
6
+
7
+ - `add_sql_db(app, url=None, dsn_env="SQL_URL")` wires the session and raises if the URL env is missing. 【F:src/svc_infra/api/fastapi/db/sql/add.py†L55-L114】
8
+ - Build URLs piecemeal with `DB_DIALECT`, `DB_DRIVER`, `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, `DB_PASSWORD`, `DB_PARAMS`, or point at `SQL_URL_FILE`/`DB_PASSWORD_FILE`. 【F:src/svc_infra/db/sql/utils.py†L85-L206】
9
+ - Alembic templates respect overrides such as `ALEMBIC_DISCOVER_PACKAGES`, `ALEMBIC_INCLUDE_SCHEMAS`, and `ALEMBIC_SKIP_DROPS`. 【F:src/svc_infra/db/sql/utils.py†L274-L347】
10
+
11
+ ## Mongo
12
+
13
+ - `add_mongo_db(app, dsn_env="MONGO_URL")` validates the URL and optional db name. 【F:src/svc_infra/api/fastapi/db/nosql/mongo/add.py†L28-L53】
14
+ - Configure via `MONGO_URL`, `MONGO_DB`, `MONGO_APPNAME`, `MONGO_MIN_POOL`, `MONGO_MAX_POOL`, or point at `MONGO_URL_FILE`. 【F:src/svc_infra/db/nosql/mongo/settings.py†L9-L13】【F:src/svc_infra/db/nosql/utils.py†L56-L113】
@@ -0,0 +1,62 @@
1
+ # Docs & SDKs
2
+
3
+ This guide shows how to enable API docs, enrich your OpenAPI, and generate SDKs.
4
+
5
+ ## Enabling docs
6
+
7
+ - Use `setup_service_api(...)` for versioned apps; root docs are auto-mounted in local/dev.
8
+ - For standalone FastAPI apps, call `add_docs(app)` to mount:
9
+ - `/docs` (Swagger UI)
10
+ - `/redoc` (ReDoc)
11
+ - `/openapi.json` (OpenAPI schema)
12
+ - A landing page at `/` listing root and scoped docs (falls back to `/_docs` if `/` is taken).
13
+
14
+ Tip: Append `?theme=dark` to `/docs` or `/redoc` for a minimal dark mode.
15
+
16
+ ## OpenAPI enrichment
17
+
18
+ The OpenAPI pipeline adds helpful metadata automatically:
19
+ - `x-codeSamples` per operation (curl and httpie) using your server base URL.
20
+ - Problem+JSON examples on error responses (4xx/5xx) referencing the `Problem` schema.
21
+ - Existing success/media examples are preserved and normalized.
22
+
23
+ These mutators are applied for both root and versioned apps via `setup_mutators(...)`.
24
+
25
+ ## Exporting OpenAPI
26
+
27
+ `add_docs(app, export_openapi_to="openapi.json")` writes the schema to disk on startup.
28
+
29
+ ## Generate SDKs (CLI)
30
+
31
+ Use the CLI to generate SDKs from OpenAPI (defaults to dry-run, uses npx tools):
32
+
33
+ - TypeScript (openapi-typescript-codegen):
34
+ svc-infra sdk ts openapi.json --outdir sdk-ts --dry-run=false
35
+
36
+ - Python (openapi-generator):
37
+ svc-infra sdk py openapi.json --outdir sdk-py --package-name client_sdk --dry-run=false
38
+
39
+ - Postman collection:
40
+ svc-infra sdk postman openapi.json --out postman.json --dry-run=false
41
+
42
+ ## Quick curl examples
43
+
44
+ Replace URL and payload as needed; these align with x-codeSamples included in the schema.
45
+
46
+ - GET
47
+ curl -X GET 'http://localhost:8000/v1/projects'
48
+
49
+ - POST with JSON
50
+ curl -X POST 'http://localhost:8000/v1/projects' \
51
+ -H 'Content-Type: application/json' \
52
+ -d '{"name":"Example"}'
53
+
54
+ Notes:
55
+ - You need Node.js; the CLI calls `npx` for generator tools. Add them to your devDependencies for reproducibility.
56
+ - For CI, export OpenAPI to a path and run the CLI with `--dry-run=false`.
57
+
58
+ ## Troubleshooting
59
+
60
+ - Docs not visible at `/`? If your app already handles `/`, the landing page is mounted at `/_docs`.
61
+ - Dark mode not applying? Use `/docs?theme=dark` or `/redoc?theme=dark`.
62
+ - Missing Problem examples? Ensure your error handlers reference the `Problem` schema and that mutators run (they are wired by default in `setup_service_api`).