gitea-saas-core#41: client-facing surface — per-client keys, checkout, /api/me, self-serve work submission, Stripe webhook
- client_api.py: ClientKeyStore (SHA-256 hashed at rest, scoped, revocable),
checkout with per-client rate limit, /api/me status, /api/me/issues submit,
signature-authenticated /api/webhooks/stripe reusing the issue-#3 seam, and
operator key-issuance routes; compound assembly guard replaces the billing
operator before_request with identical operator-route semantics so scoped
client endpoints are reachable and valid client keys get 403 on operator
routes (cross-tenant + operator denials).
- auth.py: PUBLIC_PATHS + /api/webhooks/stripe; FORBIDDEN_BODY/
forbidden_response; hash_client_key (no new compare_digest call site).
- schema.sql: client_api_keys table (hash + prefix only).
- app.py: mounts the client surface (stripe_builder/gitea injectable).
- tests/test_client_api.py (18 tests) + assembled-app coverage in test_app.py.
Full suite: 293 passed, 1 skipped.