caspian-utils 0.2.1 → 0.2.2

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.
package/dist/docs/auth.md CHANGED
@@ -305,21 +305,22 @@ Because Starlette runs the last-added middleware first, the effective request or
305
305
  5. `RPCMiddleware`
306
306
  6. route handler or RPC endpoint
307
307
 
308
- Current behavior by layer:
309
-
310
- - `SecurityHeadersMiddleware` can attach baseline response headers from `casp.runtime_security`, such as `X-Content-Type-Options`, framing policy, referrer policy, permissions policy, and production HSTS, while preserving any headers already set by the response. Do not assume this layer emits CSP or owns third-party browser resource allowlists; verify the installed package helper first.
311
- - `SessionMiddleware` provides `request.session` for the rest of the stack.
308
+ Current behavior by layer:
309
+
310
+ - `SecurityHeadersMiddleware` can attach baseline response headers from `casp.runtime_security`, such as `X-Content-Type-Options`, framing policy, referrer policy, permissions policy, and production HSTS, while preserving any headers already set by the response. Do not assume this layer emits CSP or owns third-party browser resource allowlists; verify the installed package helper first.
311
+ - `PublicFilesMiddleware` serves any existing `GET`/`HEAD` file under `public/**` before rate limiting, sessions, CSRF, auth, RPC, or page routing. It falls through for missing files and preserves restricted inline-media handling for configured upload directories.
312
+ - `SessionMiddleware` provides `request.session` for the rest of the stack.
312
313
  - `CSRFMiddleware` ensures `request.session["csrf_token"]` exists and emits a scoped CSRF cookie based on `pp_csrf`, for example `pp_csrf_5091` in development or plain `pp_csrf` when no dev scope is active.
313
- - `AuthMiddleware` sets request context with `Auth.set_request(request)`, initializes `StateManager`, runs provider callbacks, skips configured static asset paths, and enforces public, auth, private, and role-based route redirects.
314
+ - `AuthMiddleware` sets request context with `Auth.set_request(request)`, initializes `StateManager`, runs provider callbacks, and enforces public, auth, private, and role-based route redirects. Existing public files never reach it because `PublicFilesMiddleware` serves them first.
314
315
  - `RPCMiddleware` handles `POST` requests with `X-PP-RPC: true` and forwards them to Caspian's RPC handler after auth and session setup are already available.
315
316
 
316
317
  Keep `SessionMiddleware` immediately inside any outer response-header wrapper such as `SecurityHeadersMiddleware`. If CSRF, auth, or RPC handling runs before the session layer, `request.session` will not be available.
317
318
 
318
319
  ## Current `AuthMiddleware` Flow
319
320
 
320
- The pasted `main.py` auth middleware currently behaves like this:
321
-
322
- - bypasses auth checks for `/css/*`, `/js/*`, `/assets/*`, and `/favicon.ico`
321
+ The pasted `main.py` auth middleware currently behaves like this:
322
+
323
+ - receives no request for an existing public file because the outer `PublicFilesMiddleware` has already served it; missing paths continue through normal auth and routing behavior
323
324
  - initializes request-bound auth state with `StateManager.init(request)` and `Auth.set_request(request)`
324
325
  - runs OAuth provider signin and callback handling before public or private route checks
325
326
  - lets public routes through immediately
@@ -39,7 +39,7 @@ Important current `main.py` behaviors AI should keep in mind:
39
39
  - `register_single_route(...)` passes path params to `page()` as one positional dict, injects matching query params by name, and injects `request` by keyword when declared.
40
40
  - The render pipeline is `transform_components(...)`, then `render_with_nested_layouts(...)`, then `transform_scripts(...)`.
41
41
  - Route-level generators returned from `page()` are wrapped in `SSE(...)` before the response is sent.
42
- - Safe public-file helpers live in `casp.runtime_security` and should reject `..` traversal before serving from `public/**`.
42
+ - Safe public-file helpers live in `casp.runtime_security`. `PublicFilesMiddleware` maps every existing nested file under `public/**` to the same root-relative URL without per-directory routes, rejects traversal and symlink escape, handles only `GET`/`HEAD`, and falls through when no file exists so page and mounted-app routing still works. User-upload directories must retain their restricted inline-media policy.
43
43
  - Session middleware secrets may be resolved through `casp.runtime_security` so production can fail fast when `AUTH_SECRET` is missing or still on a default placeholder.
44
44
  - Baseline response headers are built by `casp.runtime_security` and attached through `SecurityHeadersMiddleware`. That set now includes a Content-Security-Policy from `build_content_security_policy()`, which the `CONTENT_SECURITY_POLICY` environment variable replaces wholesale. The policy must keep `'unsafe-eval'` and `'unsafe-inline'` in `script-src`, because the PulsePoint runtime compiles templates with `new Function`; read the current helper before tightening it. Outside production the helper also adds loopback origins to `connect-src` so the BrowserSync live-reload client, which polls a different port than the proxied page, is not blocked; an environment override replaces that too.
45
45
  - Middleware is added in source order as `RPCMiddleware`, `AuthMiddleware`, `CSRFMiddleware`, `SessionMiddleware`, `BodySizeLimitMiddleware`, `RateLimitMiddleware`, `SecurityHeadersMiddleware`, plus `RequestDiagnosticsMiddleware` outside production. The effective request order is reversed at runtime, so security headers and the rate limit run first and `RPCMiddleware` runs last. Verify the current list in `main.py` rather than trusting this order.
@@ -180,9 +180,14 @@ This folder contains your database model definitions in `schema.prisma` and any
180
180
 
181
181
  ### `public/`
182
182
 
183
- Store static assets here, including images, fonts, and generated frontend assets that should be served directly.
184
-
185
- Runtime-uploaded public blobs can also live here. Confirm the actual upload path in the project code, commonly `public/uploads/`, and keep that directory aligned with any BrowserSync ignore rules.
183
+ Store static assets here, including images, fonts, and generated frontend assets that should be served directly.
184
+
185
+ The public root maps directly to the URL root. Any existing nested file is
186
+ available at the same root-relative URL—for example,
187
+ `public/icons/app.png` is served as `/icons/app.png`. Adding a public
188
+ subdirectory does not require a matching route or mount in `main.py`.
189
+
190
+ Runtime-uploaded public blobs can also live here. Confirm the actual upload path in the project code, commonly `public/uploads/`, and keep that directory aligned with any BrowserSync ignore rules.
186
191
 
187
192
  If the upload directory is created only at runtime, create it on demand in the owning upload helper instead of assuming it is already committed.
188
193
 
@@ -218,7 +223,14 @@ Use this package module when the task is about:
218
223
  - user-facing vs production-safe exception messaging helpers
219
224
  - baseline response headers such as permissions, referrer, MIME sniffing, framing, or HSTS behavior
220
225
 
221
- Because this file is framework-owned, edit it only for Caspian runtime work or when documentation must match the installed package. App-specific auth policy still belongs in `src/lib/auth/auth_config.py`, and app-specific upload or storage behavior should live in route-owned code or other `src/lib/**` helpers.
226
+ Because this file is framework-owned, edit it only for Caspian runtime work or when documentation must match the installed package. App-specific auth policy still belongs in `src/lib/auth/auth_config.py`, and app-specific upload or storage behavior should live in route-owned code or other `src/lib/**` helpers.
227
+
228
+ `PublicFilesMiddleware` serves only existing `GET`/`HEAD` files that resolve
229
+ inside the configured public root and falls through for every other request.
230
+ Install it inside the security-header layer and outside rate limiting, sessions,
231
+ CSRF, auth, RPC, and page routing. Preserve a restricted inline-media policy for
232
+ user-upload directories so executable uploads such as HTML and SVG download
233
+ instead of rendering with the application's origin.
222
234
 
223
235
  ### `caspian.config.json`
224
236
 
@@ -62,7 +62,7 @@ The script is composed so the export always runs against a **fresh route/compone
62
62
 
63
63
  - boots the real ASGI app in-process and requests every route through Starlette's `TestClient`, so exported HTML is byte-identical to what the dev server serves (layouts, components, PulsePoint deferral, security headers — the whole pipeline),
64
64
  - writes each page to `static/<route>/index.html` (`/` → `static/index.html`),
65
- - mirrors public asset trees (`css`, `js`, `assets`, `uploads`, `favicon.ico`) into `static/`,
65
+ - mirrors the complete `public/**` tree into `static/`, including app-defined directories such as `icons` and `fonts`,
66
66
  - runs in `APP_ENV=development` so the build does not require production secrets.
67
67
 
68
68
  **Scope policy is "warn & skip"** — anything that cannot be fully static is reported and NOT written, so nothing broken ships silently:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "caspian-utils",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Caspian tooling",
5
5
  "main": "index.js",
6
6
  "scripts": {