SPEC v0.28: web client programme (D-129 to D-134), plans W0 to W7
New SPEC §15 Web client and FR-21; §1, §2, G-5, AC-14.1 revised; §8.5 and §10.5.2 gate lines for /app/, the invite accept and bearer requests (except /health, /setup/, /i/, /apk/) plus the /app/ CSP via $kvitt_csp, identical in infra/npm/kvitt.conf and infra/README.md step 4 (checked with nginx 1.31.7); §10.3, §10.6, §11.1 to §11.3 (rows W0 to W7), §14.7 ownership; change log is now §16. PLAN-W0..W7 and prompts/W0..W7; HANDOFF numbering D-139 / v0.30. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
This commit is contained in:
+15
-2
@@ -1,4 +1,4 @@
|
||||
# Kvitt (SPEC §10.5.2, D-120, D-126). NPM, Proxy Host <KVITT_PUBLIC_HOST>, Advanced tab, Custom Nginx Configuration.
|
||||
# Kvitt (SPEC §10.5.2, D-120, D-126, D-130). NPM, Proxy Host <KVITT_PUBLIC_HOST>, Advanced tab, Custom Nginx Configuration.
|
||||
# App gate (SPEC §8.5, D-126): outside the home LAN only a request carrying the key built into the app passes;
|
||||
# everything else gets the connection closed (444: nothing sent, nothing to fingerprint). Put the key from
|
||||
# android/local.properties (kvitt.appKey) in place of KVITT_APP_KEY. To rotate, add a second if-line with the new key.
|
||||
@@ -8,6 +8,15 @@ set $kvitt_gate 0;
|
||||
if ($http_x_kvitt_app = "KVITT_APP_KEY") { set $kvitt_gate 1; }
|
||||
if ($realip_remote_addr ~ "^192\.168\.1\.") { set $kvitt_gate 1; }
|
||||
if ($uri ~ "^/\.well-known/acme-challenge/") { set $kvitt_gate 1; }
|
||||
# Web client (SPEC §15, D-130): its files under /app/, the invite accept it logs in with, and any request carrying a
|
||||
# bearer token (the server checks the token; unknown tokens are rate-limited per IP), except /health, /setup/, /i/
|
||||
# and /apk/, which stay LAN-or-key only.
|
||||
if ($uri ~ "^/app(/|$)") { set $kvitt_gate 1; }
|
||||
if ($uri = "/invites/accept") { set $kvitt_gate 1; }
|
||||
set $kvitt_bearer 0;
|
||||
if ($http_authorization ~ "^Bearer ") { set $kvitt_bearer 1; }
|
||||
if ($uri ~ "^/(health|setup/|i/|apk/)") { set $kvitt_bearer 0; }
|
||||
if ($kvitt_bearer = 1) { set $kvitt_gate 1; }
|
||||
if ($kvitt_gate = 0) { return 444; }
|
||||
# Server body: 1 MiB, PUT /me/picture 5 MiB (SPEC §8.6); the server answers anything larger with its own 413.
|
||||
client_max_body_size 6m;
|
||||
@@ -20,7 +29,11 @@ proxy_hide_header Content-Security-Policy;
|
||||
proxy_hide_header X-Content-Type-Options;
|
||||
proxy_hide_header X-Frame-Options;
|
||||
proxy_hide_header Referrer-Policy;
|
||||
add_header Content-Security-Policy "default-src 'none'; style-src 'unsafe-inline'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'" always;
|
||||
# The web client under /app/ (D-132) needs its own scripts, styles, images and API calls: one variable, still one
|
||||
# server-level add_header.
|
||||
set $kvitt_csp "default-src 'none'; style-src 'unsafe-inline'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'";
|
||||
if ($uri ~ "^/app(/|$)") { set $kvitt_csp "default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' blob: data:; font-src 'self'; connect-src 'self'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'"; }
|
||||
add_header Content-Security-Policy $kvitt_csp always;
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
add_header X-Frame-Options DENY always;
|
||||
add_header Referrer-Policy no-referrer always;
|
||||
|
||||
Reference in New Issue
Block a user