svc-infra 0.1.621__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 +102 -179
  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.621.dist-info → svc_infra-0.1.623.dist-info}/METADATA +16 -16
  31. {svc_infra-0.1.621.dist-info → svc_infra-0.1.623.dist-info}/RECORD +33 -5
  32. {svc_infra-0.1.621.dist-info → svc_infra-0.1.623.dist-info}/WHEEL +0 -0
  33. {svc_infra-0.1.621.dist-info → svc_infra-0.1.623.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,155 @@
1
+ # Security: Configuration & Examples
2
+
3
+ This guide covers the security primitives built into svc-infra and how to wire them:
4
+
5
+ > ℹ️ Environment variables for the auth/security helpers are catalogued in [Environment Reference](environment.md).
6
+
7
+ - Password policy and breach checking
8
+ - Account lockout (exponential backoff)
9
+ - Sessions and refresh tokens (rotation + revocation)
10
+ - JWT key rotation
11
+ - Signed cookies
12
+ - CORS and security headers
13
+ - RBAC and ABAC
14
+ - MFA policy hooks
15
+
16
+ Module map (examples reference these):
17
+ - `svc_infra.security.lockout` (LockoutConfig, compute_lockout, record_attempt, get_lockout_status)
18
+ - `svc_infra.security.signed_cookies` (sign_cookie, verify_cookie)
19
+ - `svc_infra.security.audit` and `security.audit_service` (hash-chain audit logs)
20
+ - `svc_infra.api.fastapi.auth.gaurd` (password login with lockout checks)
21
+ - `svc_infra.api.fastapi.auth.routers.*` (sessions, oauth routes, etc.)
22
+ - `svc_infra.api.fastapi.auth.settings.get_auth_settings` (cookie + token settings)
23
+ - `svc_infra.api.fastapi.middleware.security_headers` and CORS setup (strict defaults)
24
+
25
+ ## Password policy and breach checking
26
+ - Enforced by validators with a configurable policy.
27
+ - Breach checking uses the HIBP k-Anonymity range API; can be toggled via settings.
28
+
29
+ Example toggles (pseudo-config):
30
+ - `AUTH_PASSWORD_MIN_LENGTH=12`
31
+ - `AUTH_PASSWORD_REQUIRE_SYMBOL=True`
32
+ - `AUTH_PASSWORD_BREACH_CHECK=True`
33
+
34
+ ## Account lockout
35
+ - Exponential backoff with a max cooldown cap to deter credential stuffing.
36
+ - Attempts tracked by user_id and/or IP hash.
37
+ - Login endpoint blocks with 429 + `Retry-After` during cooldown.
38
+
39
+ Key API (from `svc_infra.security.lockout`):
40
+ - `LockoutConfig(threshold=5, window_minutes=15, base_cooldown_seconds=30, max_cooldown_seconds=3600)`
41
+ - `compute_lockout(fail_count, cfg)` → `LockoutStatus(locked, next_allowed_at, failure_count)`
42
+ - `record_attempt(session, user_id, ip_hash, success)`
43
+ - `get_lockout_status(session, user_id, ip_hash, cfg)`
44
+
45
+ Login integration (simplified):
46
+ ```python
47
+ from svc_infra.security.lockout import get_lockout_status, record_attempt
48
+
49
+ # Compute ip_hash from request.client.host
50
+ status = await get_lockout_status(session, user_id=None, ip_hash=ip_hash)
51
+ if status.locked:
52
+ raise HTTPException(429, headers={"Retry-After": ..})
53
+
54
+ user = await user_manager.user_db.get_by_email(email)
55
+ if not user:
56
+ await record_attempt(session, user_id=None, ip_hash=ip_hash, success=False)
57
+ raise HTTPException(400, "LOGIN_BAD_CREDENTIALS")
58
+ ```
59
+
60
+ ## Sessions and refresh tokens
61
+ - Sessions are enumerable and revocable via the sessions router.
62
+ - Refresh tokens are rotated; old tokens are invalidated via a revocation list.
63
+
64
+ Operational notes:
65
+ - Persist sessions/tokens in a durable DB.
66
+ - Favor short access token TTLs if refresh flow is robust.
67
+
68
+ ## JWT key rotation
69
+ - Primary secret plus `old_secrets` allow seamless rotation.
70
+ - Set environment variables:
71
+ - `AUTH_JWT__SECRET="..."`
72
+ - `AUTH_JWT__OLD_SECRETS="old1,old2"`
73
+
74
+ ## Signed cookies
75
+ Module: `svc_infra.security.signed_cookies`
76
+
77
+ ```python
78
+ from svc_infra.security.signed_cookies import sign_cookie, verify_cookie
79
+
80
+ sig = sign_cookie({"sub": "user-123"}, secret="k1", exp_seconds=3600)
81
+ payload = verify_cookie(sig, secret="k1", old_secrets=["k0"]) # returns dict
82
+ ```
83
+
84
+ ## CORS and security headers
85
+ - Strict CORS defaults (deny by default). Provide allowlist entries.
86
+ - Security headers middleware sets common protections (X-Frame-Options, X-Content-Type-Options, etc.).
87
+
88
+ Use `svc_infra.security.add.add_security` to install the default middlewares on any
89
+ FastAPI app. By default it adds:
90
+
91
+ - `SecurityHeadersMiddleware` with strict defaults (HSTS, X-Frame-Options, etc.).
92
+ - A strict `CORSMiddleware` that only enables CORS when origins are provided (via
93
+ parameters or environment variables such as `CORS_ALLOW_ORIGINS`).
94
+
95
+ The helper also supports optional toggles so you can match the same cookie and
96
+ header configuration that `setup_service_api` uses.
97
+
98
+ ```python
99
+ from fastapi import FastAPI
100
+
101
+ from svc_infra.security.add import add_security
102
+
103
+ app = FastAPI()
104
+
105
+ add_security(
106
+ app,
107
+ cors_origins=["https://app.example.com"],
108
+ headers_overrides={"Content-Security-Policy": "default-src 'self'"},
109
+ install_session_middleware=True, # adds Starlette's SessionMiddleware
110
+ )
111
+ ```
112
+
113
+ Environment variables (applied when parameters are omitted):
114
+
115
+ | Variable | Purpose |
116
+ | --- | --- |
117
+ | `CORS_ALLOW_ORIGINS` | Comma-separated CORS origins (e.g. `https://app.example.com, https://admin.example.com`) |
118
+ | `CORS_ALLOW_METHODS` | Allowed HTTP methods (defaults to `*`) |
119
+ | `CORS_ALLOW_HEADERS` | Allowed headers (defaults to `*`) |
120
+ | `CORS_ALLOW_ORIGIN_REGEX` | Regex used when matching origins (ignored if not set) |
121
+ | `CORS_ALLOW_CREDENTIALS` | Toggle credentials support (`true` / `false`) |
122
+ | `SESSION_COOKIE_NAME` | Session cookie name (defaults to `svc_session`) |
123
+ | `SESSION_COOKIE_MAX_AGE_SECONDS` | Max age for the session cookie (defaults to `14400`) |
124
+ | `SESSION_COOKIE_SAMESITE` | SameSite policy (`lax` by default) |
125
+ | `SESSION_COOKIE_SECURE` | Force the session cookie to be HTTPS-only |
126
+ | `SESSION_SECRET` | Secret key for Starlette's SessionMiddleware |
127
+
128
+ When your service already uses `setup_service_api`, call `add_security` after
129
+ building the parent app if you need additional overrides while keeping the
130
+ defaults intact:
131
+
132
+ ```python
133
+ from svc_infra.api.fastapi.setup import setup_service_api
134
+ from svc_infra.security.add import add_security
135
+
136
+ app = setup_service_api(...)
137
+
138
+ add_security(
139
+ app,
140
+ headers_overrides={"Strict-Transport-Security": "max-age=63072000; includeSubDomains"},
141
+ enable_hsts_preload=False,
142
+ )
143
+ ```
144
+
145
+ ## RBAC and ABAC
146
+ - RBAC decorators guard endpoints by role/permission.
147
+ - ABAC evaluates resource ownership and attributes (e.g., `owns_resource`).
148
+
149
+ ## MFA policy hooks
150
+ - Policy decides when MFA is required; login returns 401 with `MFA_REQUIRED` and a pre-token when applicable.
151
+
152
+ ## Troubleshooting
153
+ - 429 on login: lockout active. Check `Retry-After` and `FailedAuthAttempt` rows.
154
+ - Token invalid post-refresh: confirm rotation + revocation writes.
155
+ - Cookie verification errors: check signing keys/exp.
@@ -0,0 +1,35 @@
1
+ # Tenancy model and integration
2
+
3
+ This framework uses a soft-tenant isolation model by default: tenant_id is a column on tenant-scoped tables, and all queries are filtered by this value. Consumers can later adopt schema-per-tenant or DB-per-tenant strategies; the API surfaces remain compatible.
4
+
5
+ ## How tenant is resolved
6
+ - `resolve_tenant_id(request)` looks up tenant id in this order:
7
+ 1) Global override hook (set via `add_tenancy(app, resolver=...)`)
8
+ 2) Auth identity (user.tenant_id or api_key.tenant_id) when auth is enabled
9
+ 3) `X-Tenant-Id` request header
10
+ 4) `request.state.tenant_id`
11
+
12
+ Use `TenantId` dependency to require it in routes, and `OptionalTenantId` to access it if present.
13
+
14
+ ## Enforcement in data layer
15
+ - Wrap services with `TenantSqlService` to automatically:
16
+ - Apply `WHERE model.tenant_id == <tenant>` on list/get/update/delete/search/count.
17
+ - Inject `tenant_id` upon create when the model has the tenant field.
18
+
19
+ ## Tenant-aware CRUD router
20
+ - When defining a `SqlResource`, set `tenant_field="tenant_id"` to mount a tenant-aware CRUD router. All endpoints will require `TenantId` and enforce scoping.
21
+
22
+ ## Per-tenant rate limits / quotas
23
+ - Global middleware and per-route dependency support tenant-aware policies:
24
+ - `scope_by_tenant=True` puts requests in independent buckets per tenant.
25
+ - `limit_resolver(request, tenant_id)` lets you return dynamic limits (e.g., plan-based quotas).
26
+
27
+ ## Export a tenant’s data (SQL)
28
+ - CLI command: `sql export-tenant`
29
+ - Example:
30
+ - `python -m svc_infra.cli sql export-tenant items --tenant-id t1 --output out.json`
31
+ - Flags:
32
+ - `--tenant-id` (required), `--tenant-field` (default `tenant_id`), `--limit` (optional), `--database-url` (or set `SQL_URL`).
33
+
34
+ ## Migration to other isolation strategies
35
+ - Schema-per-tenant or DB-per-tenant can be layered by adapting the session factory or repository to select the schema/DB based on `tenant_id`. Your application code that relies on `TenantId` and tenant-aware services/routers remains the same.
@@ -0,0 +1,112 @@
1
+ # Webhooks Framework
2
+
3
+ This module provides primitives to publish events to external consumers via webhooks, verify inbound signatures, and handle robust retries using the shared JobQueue and Outbox patterns.
4
+
5
+ > ℹ️ Webhook helper environment expectations live in [Environment Reference](environment.md).
6
+
7
+ ## Quickstart
8
+
9
+ - Subscriptions and publishing:
10
+
11
+ ```python
12
+ from svc_infra.webhooks.service import InMemoryWebhookSubscriptions, WebhookService
13
+ from svc_infra.db.outbox import InMemoryOutboxStore
14
+
15
+ subs = InMemoryWebhookSubscriptions()
16
+ subs.add("invoice.created", "https://example.com/webhook", "sekrit")
17
+ svc = WebhookService(outbox=InMemoryOutboxStore(), subs=subs)
18
+ svc.publish("invoice.created", {"id": "inv_1", "version": 1})
19
+ ```
20
+
21
+ - Delivery worker and headers:
22
+
23
+ ```python
24
+ from svc_infra.jobs.builtins.webhook_delivery import make_webhook_handler
25
+ from svc_infra.jobs.worker import process_one
26
+
27
+ handler = make_webhook_handler(
28
+ outbox=..., inbox=..., get_webhook_url_for_topic=lambda t: url, get_secret_for_topic=lambda t: secret,
29
+ )
30
+ # process_one(queue, handler) will POST JSON with headers:
31
+ # X-Event-Id, X-Topic, X-Attempt, X-Signature (HMAC-SHA256), X-Signature-Alg, X-Signature-Version, X-Payload-Version
32
+ ```
33
+
34
+ - Verification (FastAPI):
35
+
36
+ ```python
37
+ from fastapi import Depends, FastAPI
38
+ from svc_infra.webhooks.fastapi import require_signature
39
+ from svc_infra.webhooks.signing import sign
40
+
41
+ app = FastAPI()
42
+ app.post("/webhook")(lambda body=Depends(require_signature(lambda: ["old","new"])): {"ok": True})
43
+ ```
44
+
45
+ ## FastAPI wiring
46
+
47
+ - Attach the router with shared in-memory stores (great for tests / local runs):
48
+
49
+ ```python
50
+ from fastapi import FastAPI
51
+
52
+ from svc_infra.webhooks import add_webhooks
53
+
54
+ app = FastAPI()
55
+ add_webhooks(app)
56
+ ```
57
+
58
+ - Respect environment overrides for Redis-backed stores by exporting `REDIS_URL`
59
+ and selecting the backend via `WEBHOOKS_OUTBOX=redis` (optional
60
+ `WEBHOOKS_INBOX=redis` for the dedupe store). The helper records the chosen
61
+ instances on `app.state` for further customisation:
62
+
63
+ ```python
64
+ import os
65
+
66
+ os.environ["WEBHOOKS_OUTBOX"] = "redis"
67
+ os.environ["WEBHOOKS_INBOX"] = "redis"
68
+
69
+ app = FastAPI()
70
+ add_webhooks(app) # creates RedisOutboxStore / RedisInboxStore when redis-py is available
71
+
72
+ # Later you can inspect or extend behaviour:
73
+ app.state.webhooks_subscriptions.add("invoice.created", "https://example.com/webhook", "sekrit")
74
+ ```
75
+
76
+ - Provide explicit overrides (e.g. dependency-injected SQL stores) or reuse your
77
+ existing job queue / scheduler. Passing a queue automatically registers the
78
+ outbox tick and delivery handler so your worker loop can process jobs:
79
+
80
+ ```python
81
+ from svc_infra.jobs.easy import easy_jobs
82
+
83
+ queue, scheduler = easy_jobs()
84
+
85
+ add_webhooks(
86
+ app,
87
+ outbox=my_outbox_store,
88
+ inbox=lambda: my_inbox_store, # factories are supported
89
+ queue=queue,
90
+ scheduler=scheduler,
91
+ )
92
+
93
+ # scheduler.add_task(...) is handled internally when both queue and scheduler are supplied
94
+ ```
95
+
96
+ ## Runner wiring
97
+
98
+ If you prefer explicit wiring, you can still register the tick manually:
99
+
100
+ ```python
101
+ from svc_infra.jobs.easy import easy_jobs
102
+ from svc_infra.jobs.builtins.outbox_processor import make_outbox_tick
103
+
104
+ queue, scheduler = easy_jobs() # uses JOBS_DRIVER and REDIS_URL
105
+ scheduler.add_task("outbox", 1, make_outbox_tick(outbox_store, queue))
106
+ # Start runner: `svc-infra jobs run`
107
+ ```
108
+
109
+ ## Notes
110
+ - Retries/backoff are handled by the JobQueue; delivery marks Inbox after success to prevent duplicates.
111
+ - For production subscriptions and inbox/outbox, provide persistent implementations and override DI in your app.
112
+ - Signature rotation supported via `verify_any` and FastAPI dependency accepting multiple secrets.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: svc-infra
3
- Version: 0.1.621
3
+ Version: 0.1.623
4
4
  Summary: Infrastructure for building and deploying prod-ready services
5
5
  License: MIT
6
6
  Keywords: fastapi,sqlalchemy,alembic,auth,infra,async,pydantic
@@ -77,7 +77,7 @@ Description-Content-Type: text/markdown
77
77
  # svc-infra
78
78
 
79
79
  [![PyPI](https://img.shields.io/pypi/v/svc-infra.svg)](https://pypi.org/project/svc-infra/)
80
- [![Docs](https://img.shields.io/badge/docs-reference-blue)](docs/)
80
+ [![Docs](https://img.shields.io/badge/docs-reference-blue)](src/svc_infra/docs/)
81
81
 
82
82
  svc-infra packages the shared building blocks we use to ship production FastAPI services fast—HTTP APIs with secure auth, durable persistence, background execution, cache, observability, and webhook plumbing that all share the same batteries-included defaults.
83
83
 
@@ -85,17 +85,17 @@ svc-infra packages the shared building blocks we use to ship production FastAPI
85
85
 
86
86
  | Helper | What it covers | Guide |
87
87
  | --- | --- | --- |
88
- | API | FastAPI bootstrap, envelopes, middleware, docs wiring | [FastAPI guide](docs/api.md) |
89
- | Auth | Sessions, OAuth/OIDC, MFA, SMTP delivery | [Auth settings](docs/auth.md) |
90
- | Database | SQL + Mongo wiring, Alembic helpers, inbox/outbox patterns | [Database guide](docs/database.md) |
91
- | Jobs | JobQueue, scheduler, CLI worker | [Jobs quickstart](docs/jobs.md) |
92
- | Cache | cashews decorators, namespace management, TTL helpers | [Cache guide](docs/cache.md) |
93
- | Observability | Prometheus middleware, Grafana automation, OTEL hooks | [Observability guide](docs/observability.md) |
94
- | Ops | Probes, breaker, SLOs & dashboards | [SLOs & Ops](docs/ops.md) |
95
- | Webhooks | Subscription store, signing, retry worker | [Webhooks framework](docs/webhooks.md) |
96
- | Security | Password policy, lockout, signed cookies, headers | [Security hardening](docs/security.md) |
97
- | Contributing | Dev setup and quality gates | [Contributing guide](docs/contributing.md) |
98
- | Data Lifecycle | Fixtures, retention, erasure, backups | [Data lifecycle](docs/data-lifecycle.md) |
88
+ | API | FastAPI bootstrap, envelopes, middleware, docs wiring | [FastAPI guide](src/svc_infra/docs/api.md) |
89
+ | Auth | Sessions, OAuth/OIDC, MFA, SMTP delivery | [Auth settings](src/svc_infra/docs/auth.md) |
90
+ | Database | SQL + Mongo wiring, Alembic helpers, inbox/outbox patterns | [Database guide](src/svc_infra/docs/database.md) |
91
+ | Jobs | JobQueue, scheduler, CLI worker | [Jobs quickstart](src/svc_infra/docs/jobs.md) |
92
+ | Cache | cashews decorators, namespace management, TTL helpers | [Cache guide](src/svc_infra/docs/cache.md) |
93
+ | Observability | Prometheus middleware, Grafana automation, OTEL hooks | [Observability guide](src/svc_infra/docs/observability.md) |
94
+ | Ops | Probes, breaker, SLOs & dashboards | [SLOs & Ops](src/svc_infra/docs/ops.md) |
95
+ | Webhooks | Subscription store, signing, retry worker | [Webhooks framework](src/svc_infra/docs/webhooks.md) |
96
+ | Security | Password policy, lockout, signed cookies, headers | [Security hardening](src/svc_infra/docs/security.md) |
97
+ | Contributing | Dev setup and quality gates | [Contributing guide](src/svc_infra/docs/contributing.md) |
98
+ | Data Lifecycle | Fixtures, retention, erasure, backups | [Data lifecycle](src/svc_infra/docs/data-lifecycle.md) |
99
99
 
100
100
  ## Minimal FastAPI bootstrap
101
101
 
@@ -123,9 +123,9 @@ async def handle_webhook(payload = Depends(require_signature(lambda: ["current",
123
123
  - **API** – toggle logging/observability and docs exposure with `ENABLE_LOGGING`, `LOG_LEVEL`, `LOG_FORMAT`, `ENABLE_OBS`, `METRICS_PATH`, `OBS_SKIP_PATHS`, and `CORS_ALLOW_ORIGINS`. 【F:src/svc_infra/api/fastapi/ease.py†L67-L111】【F:src/svc_infra/api/fastapi/setup.py†L47-L88】
124
124
  - **Auth** – configure JWT secrets, SMTP, cookies, and policy using the `AUTH_…` settings family (e.g., `AUTH_JWT__SECRET`, `AUTH_SMTP_HOST`, `AUTH_SESSION_COOKIE_SECURE`). 【F:src/svc_infra/api/fastapi/auth/settings.py†L23-L91】
125
125
  - **Database** – set connection URLs or components via `SQL_URL`/`SQL_URL_FILE`, `DB_DIALECT`, `DB_HOST`, `DB_USER`, `DB_PASSWORD`, plus Mongo knobs like `MONGO_URL`, `MONGO_DB`, and `MONGO_URL_FILE`. 【F:src/svc_infra/api/fastapi/db/sql/add.py†L55-L114】【F:src/svc_infra/db/sql/utils.py†L85-L206】【F:src/svc_infra/db/nosql/mongo/settings.py†L9-L13】【F:src/svc_infra/db/nosql/utils.py†L56-L113】
126
- - **Jobs** – choose the queue backend with `JOBS_DRIVER` and provide Redis via `REDIS_URL`; interval schedules can be declared with `JOBS_SCHEDULE_JSON`. 【F:src/svc_infra/jobs/easy.py†L11-L27】【F:docs/jobs.md†L11-L48】
126
+ - **Jobs** – choose the queue backend with `JOBS_DRIVER` and provide Redis via `REDIS_URL`; interval schedules can be declared with `JOBS_SCHEDULE_JSON`. 【F:src/svc_infra/jobs/easy.py†L11-L27】【F:src/svc_infra/docs/jobs.md†L11-L48】
127
127
  - **Cache** – namespace keys and lifetimes through `CACHE_PREFIX`, `CACHE_VERSION`, and TTL overrides `CACHE_TTL_DEFAULT`, `CACHE_TTL_SHORT`, `CACHE_TTL_LONG`. 【F:src/svc_infra/cache/README.md†L20-L173】【F:src/svc_infra/cache/ttl.py†L26-L55】
128
128
  - **Observability** – turn metrics on/off or adjust scrape paths with `ENABLE_OBS`, `METRICS_PATH`, `OBS_SKIP_PATHS`, and Prometheus/Grafana flags like `SVC_INFRA_DISABLE_PROMETHEUS`, `SVC_INFRA_RATE_WINDOW`, `SVC_INFRA_DASHBOARD_REFRESH`, `SVC_INFRA_DASHBOARD_RANGE`. 【F:src/svc_infra/api/fastapi/ease.py†L67-L111】【F:src/svc_infra/obs/metrics/asgi.py†L49-L206】【F:src/svc_infra/obs/cloud_dash.py†L85-L108】
129
- - **Webhooks** – reuse the jobs envs (`JOBS_DRIVER`, `REDIS_URL`) for the delivery worker and queue configuration. 【F:docs/webhooks.md†L32-L53】
130
- - **Security** – enforce password policy, MFA, and rotation with auth prefixes such as `AUTH_PASSWORD_MIN_LENGTH`, `AUTH_PASSWORD_REQUIRE_SYMBOL`, `AUTH_JWT__SECRET`, and `AUTH_JWT__OLD_SECRETS`. 【F:docs/security.md†L24-L70】
129
+ - **Webhooks** – reuse the jobs envs (`JOBS_DRIVER`, `REDIS_URL`) for the delivery worker and queue configuration. 【F:src/svc_infra/docs/webhooks.md†L32-L53】
130
+ - **Security** – enforce password policy, MFA, and rotation with auth prefixes such as `AUTH_PASSWORD_MIN_LENGTH`, `AUTH_PASSWORD_REQUIRE_SYMBOL`, `AUTH_JWT__SECRET`, and `AUTH_JWT__OLD_SECRETS`. 【F:src/svc_infra/docs/security.md†L24-L70】
131
131
 
@@ -142,7 +142,7 @@ svc_infra/cli/cmds/db/sql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
142
142
  svc_infra/cli/cmds/db/sql/alembic_cmds.py,sha256=uCreHg69Zf6B5gbv9Dm39jCRk6q2KQy_05A-75IP0Fg,9650
143
143
  svc_infra/cli/cmds/db/sql/sql_export_cmds.py,sha256=YpkguUJFeFApMphVkhOJllTi25ejlsQaJarMe6vJD54,2685
144
144
  svc_infra/cli/cmds/db/sql/sql_scaffold_cmds.py,sha256=MKc_T_tY1Y_wQl7XTlq8GhYWMMI1q1_vcFZVPOEcNUg,4601
145
- svc_infra/cli/cmds/docs/docs_cmds.py,sha256=fbdnTEfzx3HNNXK3zL5fa_Lk3FowzHBcfS_VHQah7VQ,10720
145
+ svc_infra/cli/cmds/docs/docs_cmds.py,sha256=Gyi0cr22-SY8kThAutRSzW9LmZ1ek9LTpwFpvlclKWM,6407
146
146
  svc_infra/cli/cmds/dx/__init__.py,sha256=wQtl3-kOgoESlpVkjl3YFtqkOnQSIvVsOdutiaZFejM,197
147
147
  svc_infra/cli/cmds/dx/dx_cmds.py,sha256=XTKUJzS3UIYn6h3CHzDEWKYJaWn0TzGiUCq3OeW27E0,3326
148
148
  svc_infra/cli/cmds/help.py,sha256=wGfZFMYaR2ZPwW2JwKDU7M3m4AtdCd8GRQ412AmEBUM,758
@@ -216,6 +216,34 @@ svc_infra/db/sql/uniq_hooks.py,sha256=6gCnO0_Y-rhB0p-VuY0mZ9m1u3haiLWI3Ns_iUTqF_
216
216
  svc_infra/db/sql/utils.py,sha256=nzuDcDhnVNehx5Y9BZLgxw8fvpfYbxTfXQsgnznVf4w,32862
217
217
  svc_infra/db/sql/versioning.py,sha256=okZu2ad5RAFXNLXJgGpcQvZ5bc6gPjRWzwiBT0rEJJw,400
218
218
  svc_infra/db/utils.py,sha256=aTD49VJSEu319kIWJ1uijUoP51co4lNJ3S0_tvuyGio,802
219
+ svc_infra/docs/acceptance-matrix.md,sha256=1J0722pdxNGVtjI5tAZA2wzzr1UiagjvPgzB8zo0Ks8,2383
220
+ svc_infra/docs/acceptance.md,sha256=1e9Ym4YOwnKHLfCTjx9garKlSKEnxzIYunx4cqoWhZ8,2327
221
+ svc_infra/docs/adr/0002-background-jobs-and-scheduling.md,sha256=iscWFmDmMFcaON3W1FePZT9aHvlBLWCoB4QE7iA5BBI,2418
222
+ svc_infra/docs/adr/0003-webhooks-framework.md,sha256=CFFd4RrmbTbKPr_RRuEe4zdpbpU8C6vD_DimCd702zE,1405
223
+ svc_infra/docs/adr/0004-tenancy-model.md,sha256=ZaJesiWqVggrRLbTXCIyyaVNiDDjl0NWPt7dSrj5SIQ,2732
224
+ svc_infra/docs/adr/0005-data-lifecycle.md,sha256=XLFu2I0d_6Oc7e-MOy9UE_UuCkVHCvhWy8rUlVBeAcE,4897
225
+ svc_infra/docs/adr/0006-ops-slos-and-metrics.md,sha256=Qd17l0RKGXczLs2AKJewCAxr2g0SP7BpcLLViGknJnE,2453
226
+ svc_infra/docs/adr/0007-docs-and-sdks.md,sha256=uQ-q-5omaOXPL5tW5q0_1FE9P_OmO9aDHXqWSGme3eg,4481
227
+ svc_infra/docs/adr/0008-billing-primitives.md,sha256=trqzGWsyk_QXNtVRHXcTayEV4Sx1Zjhis93bCnsqDvo,5544
228
+ svc_infra/docs/adr/0009-acceptance-harness.md,sha256=jDmoWn2uJTeK28YZo75YR1ym6NdgcmPOlMfupZlCCBs,2146
229
+ svc_infra/docs/api.md,sha256=AlPL9kBS6_dM0NrOteDQ9WqalSfKf_p9_zdy1CtGJdU,2384
230
+ svc_infra/docs/auth.md,sha256=PRl9G4UW78cT_7c4koVh5NDlheNAr02CpJT2YFbEXto,1333
231
+ svc_infra/docs/cache.md,sha256=XUsJ8p6QFBWTNuqata1HJjB6accdPANipdoNAkKirqs,673
232
+ svc_infra/docs/cli.md,sha256=w5og4SWrLyizlJAJiFgcWu2jDSc1Wj3NCYqzbbvg8VE,1702
233
+ svc_infra/docs/contributing.md,sha256=a0PhmzCLFw8S3odxFbI3p5_FOiPMZLrxk15Ujrk7ao4,1175
234
+ svc_infra/docs/data-lifecycle.md,sha256=_XFZCj9qiYgEiN9jO_lq7RcpVIeLVN7REaFziiNCnEI,2684
235
+ svc_infra/docs/database.md,sha256=p_t-4ApQqa7ImhiV6wv0oklDTEZPn-snO1K1FoNtZpI,1129
236
+ svc_infra/docs/docs-and-sdks.md,sha256=v5Uz-CVNswiZ-ITiqo6tiOX5xGJSnftfbA7b3hrqHqI,2330
237
+ svc_infra/docs/environment.md,sha256=w84-1hL3zog6fYYgDLiQRQiAlS2CadjmZmu87Frzxy8,9700
238
+ svc_infra/docs/idempotency.md,sha256=jvemdVY4g6xoHW38OAZIS5JxA3SdK8a0iAayx18kIbk,3890
239
+ svc_infra/docs/jobs.md,sha256=iyDPo6oo7fJdBZ9e6Qt9x_kX4sX7_TUUdY89CFiZ61I,1586
240
+ svc_infra/docs/observability.md,sha256=qzu44CSmGwjdLkBj0TQ1LBMmXd7BO2hmJSdByZlBXck,1021
241
+ svc_infra/docs/ops.md,sha256=to47s3Z66-wQ4WvwEnD3xMrcmvKf-lowBqWqS-2n2HQ,1306
242
+ svc_infra/docs/rate-limiting.md,sha256=W_96ch1dIjD9NP9Ma45UgsNTqDh6wlMbhJgAN3Uc4do,3983
243
+ svc_infra/docs/repo-review.md,sha256=REz2zT1LXURQUB9yZiBn9ICXHkpdpBSJCRTp-AKH190,8153
244
+ svc_infra/docs/security.md,sha256=tjr5IlBYu47Z1qs2ZHQE-Low1ZOQDtZWoKjmaNXalG4,6121
245
+ svc_infra/docs/tenancy.md,sha256=k7mOvIQWZF1b3-CETyE8UsIPdhGjNNa9Q4j5AXAqQrQ,2023
246
+ svc_infra/docs/webhooks.md,sha256=b0F2vnkxOWdYWwCBAo9i39xdi11nbgEtl05JE3e0lrE,3671
219
247
  svc_infra/dx/add.py,sha256=FAnLGP0BPm_q_VCEcpUwfj-b0mEse988chh9DHeS7GU,1474
220
248
  svc_infra/dx/changelog.py,sha256=9SD29ZzKzbGTA6kHQXiPLtb7uueL1wrRiiLE2qMzz8o,1941
221
249
  svc_infra/dx/checks.py,sha256=R6YqRvpKPr9zQgif4QVx2_Zl4s9YjehSkAvwlxK46lI,2267
@@ -296,7 +324,7 @@ svc_infra/webhooks/fastapi.py,sha256=BCNvGNxukf6dC2a4i-6en-PrjBGV19YvCWOot5lXWsA
296
324
  svc_infra/webhooks/router.py,sha256=6JvAVPMEth_xxHX-IsIOcyMgHX7g1H0OVxVXKLuMp9w,1596
297
325
  svc_infra/webhooks/service.py,sha256=hWgiJRXKBwKunJOx91C7EcLUkotDtD3Xp0RT6vj2IC0,1797
298
326
  svc_infra/webhooks/signing.py,sha256=NCwdZzmravUe7HVIK_uXK0qqf12FG-_MVsgPvOw6lsM,784
299
- svc_infra-0.1.621.dist-info/METADATA,sha256=QD-ETuukvgwtsMr9N5ylyIOBUgPW6yms4kDLJbFN1H4,8106
300
- svc_infra-0.1.621.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
301
- svc_infra-0.1.621.dist-info/entry_points.txt,sha256=6x_nZOsjvn6hRZsMgZLgTasaCSKCgAjsGhACe_CiP0U,48
302
- svc_infra-0.1.621.dist-info/RECORD,,
327
+ svc_infra-0.1.623.dist-info/METADATA,sha256=b0847FKVzDHLtuU1uxB7G6qubJkOqpztMsuZmEhK43I,8316
328
+ svc_infra-0.1.623.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
329
+ svc_infra-0.1.623.dist-info/entry_points.txt,sha256=6x_nZOsjvn6hRZsMgZLgTasaCSKCgAjsGhACe_CiP0U,48
330
+ svc_infra-0.1.623.dist-info/RECORD,,