← Console
Console/Planning/Document HTTP Gating
RESOLVING…
Envelope gating →

Document HTTP Gating

Batch 13 · server-delivered HTML render path at /api/access/render · visible → full file · restricted → file + sticky banner · blocked → stub HTML · reverse-proxy-ready pattern · honest: still not a full route interceptor
Spec Scaffold Batch 13 · v1.0 Planning A-owned Partial HTTP gating · deployment-dependent for full protection

Access · สิทธิ์

Overview

Batch 12 shipped envelope-level gating: /api/access/gate returned a decision JSON and the shell acted on it. Batch 13 adds a stronger path: /api/access/render. The server reads the target HTML file from DAS_DOCS_ROOT, evaluates access state, and returns the actual HTML (full, restricted variant, or stub) — so the response IS the page. A reverse-proxy rule routing all traffic through this endpoint achieves real HTTP-level gating.

Batch 12 ส่ง envelope · Batch 13 ยกระดับเป็น /api/access/render · server อ่านไฟล์เอง · ส่ง HTML จริงกลับไป · reverse-proxy ชี้ไปที่ endpoint นี้ = gating ระดับ HTTP จริง

Key Point

Server now returns the HTML. When routed through /api/access/render, hidden docs never leave the server. Honest: static URLs are still served by whatever hosts the files unless reverse-proxy rules are in place.
Server ส่ง HTML เอง · ถ้า route ผ่าน /api/access/render · เอกสาร hidden ไม่หลุดจาก server · หน้าเดิมยังเปิดได้ถ้าไม่มี reverse-proxy

Summary

The render endpoint is a FastAPI route that: (1) validates doc_id against path traversal, (2) resolves the current session to a profile, (3) computes access state via the same matrix as /api/access/resolve, (4) reads the file if the state is visible/restricted, or returns an inline stub if blocked. Every response carries X-DAS-Render-State and Cache-Control: private, no-store. Security: only .html under DOCS_ROOT · no .. · no absolute paths.

Envelope vs HTTP render

AspectEnvelope (Batch 12)HTTP render (Batch 13)
Path/api/access/gate/api/access/render
ReturnsJSON decision + optional stub_htmltext/html · the actual page
Client rolecooperate: show banner or swap bodyconsume: response IS the page
Protection against curlnone — shell JS is the enforcementfull when routed through this endpoint (server never sends the file body for blocked states)
Requires routing changenoyes, for full coverage (reverse-proxy rewrite)
Use casepages already using the shellproxied routes · team-shared links · fetch-and-display

Render endpoint

GET /api/access/render?doc_id=/planning/document-access-model.html
Cookie: ds_session=<body>.<sig>

200 OK
X-DAS-Render-State: visible | restricted | hidden-doc | hidden-group | not-granted
X-DAS-Render-Profile: <email>
Cache-Control: private, no-store
Content-Type: text/html

<!DOCTYPE html>… (full file OR restricted-wrapped OR stub)

Full spec: render_contract.json

Reverse-proxy pattern · the path to real HTTP gating

For true route-level protection on a deployment, wire a reverse proxy (nginx / Cloudflare Workers / HAProxy) to rewrite requests for protected paths to the render endpoint. Example nginx snippet (not implemented — platform concern):

location ~ ^/(planning/document-.*|document-groups|runtime/document-.*-service/service)\.html$ {
    proxy_pass http://das:8090/api/access/render?doc_id=$request_uri;
    proxy_set_header Cookie $http_cookie;
}

Without this, users can still request the file at its original path and a permissive static host will serve it. That's the honest remaining gap.

Security checks in the render endpoint

  • doc_id must not contain .. / absolute path / :
  • Only .html files are rendered
  • Resolved absolute path must stay under DOCS_ROOT
  • File must exist and be a regular file
  • X-DAS-Render-State header surfaces the decision for audit/debug
  • Cache-Control: private, no-store prevents shared caching of gated content

Honest limits

  • Not a full route interceptor. Unless a reverse-proxy routes requests here, the original static URL still serves.
  • No streaming. Whole file is read into memory and returned.
  • No per-section masking. A restricted doc shows the full body + banner — not useful when sections of the same doc carry different sensitivity.
  • No caching of rendered responses. Every request re-reads the file.
  • Response size limit: governed by FastAPI defaults. Large HTMLs may need chunked streaming in future batches.

Notes