← Console
Console/Planning/Document Server Gating
RESOLVING…
Auth Hardening →

Document Server Gating

Batch 12 · server-side gating envelope via /api/access/gate · returns allow_render + render_mode + minimal stub HTML · shell replaces page body when blocked · honest UI-layer + envelope-level enforcement only
Spec Scaffold Batch 12 · v1.0 Planning A-owned Selected-target enforcement · NOT global

Access · สิทธิ์

Overview

Batch 8 shipped UI-layer blurring: when a user lacked access, the shell visually masked the page. Batch 12 adds a server-issued gating envelope at GET /api/access/gate?doc_id=… that returns: render_mode (full/restricted/blocked), allow_read/share/export, bilingual banner, and an optional stub_html for blocked pages. The shell, on shelled pages, fetches this envelope and replaces document.body with the stub when render_mode=blocked.

Key Point

Server returns the stub; shell displays the stub. No more guessing at the client. Still UI-layer: the server does NOT intercept HTML delivery for legacy pages.
Server คืน stub · shell แสดง stub · ไม่ต้องเดาฝั่ง client · แต่ยังเป็นระดับ UI · legacy pages ไม่ถูก intercept ที่ระดับ HTTP

Summary

Gating runs entirely at the Document Access Service boundary. Shelled pages call /api/access/gate?include_stub=true on DOMContentLoaded. When render_mode=blocked, the shell swaps body HTML. When restricted, the shell keeps content but shows the banner + disables share/export. When full, nothing changes. Legacy pages that do not call the endpoint see no gating — documented honestly.

Gate envelope

GET /api/access/gate?doc_id=/planning/document-access-model.html&include_stub=true
Cookie: ds_session=...

=>
{
  "doc_id": "/planning/document-access-model.html",
  "group_id": "planning",
  "state": "restricted",
  "allow_render": true,
  "render_mode": "restricted",
  "allow_read": true,
  "allow_share": false,
  "allow_export": false,
  "reason_en": "Content restricted under your current access profile.",
  "reason_th": "เนื้อหาถูกจำกัดภายใต้โปรไฟล์ปัจจุบัน",
  "stub_html": null,
  "profile_id": "u-external-001",
  "email": "partner@external.example",
  "mode": "local-dev",
  "resolved_at": "2026-04-20T10:45:00Z",
  "honest_banner": { "en": "...", "th": "..." }
}

States → render_mode

visibility staterender_modestub_html when include_stub=trueclient behaviour
visiblefullnullno change
restrictedrestrictednullbanner + disable share/export
hidden-docblockedstub html returnedreplace body with stub
hidden-groupblockedstub html returnedreplace body with stub
not-grantedblockedstub html returnedreplace body with stub

Enforcement scope · ขอบเขตจริง

Enforced (shell calls /api/access/gate on load):

  • /document-groups.html
  • /planning/document-shell-standard.html
  • /planning/document-note-system.html
  • /planning/document-access-model.html
  • /planning/document-share-export.html
  • /planning/document-auth-backend.html
  • /planning/document-shell-coverage.html
  • /planning/document-note-backlog.html
  • /planning/document-management-status.html
  • /planning/document-note-backend.html (new Batch 11)
  • /planning/document-auth-hardening.html (new Batch 12)
  • /planning/document-server-gating.html (this page)
  • /runtime/document-access-service/service.html
  • /runtime/document-note-service/service.html

Not enforced:

  • All legacy planning / runtime / KB pages — no shell JS loaded
  • Batch 10 bridge pages — bridge strip only, no gate call
  • Tourist demos (/journey.html, /demo_*, etc.) — explicitly not-included

Honest limits

  • Server does NOT intercept HTML delivery. A user who knows the URL and has the static file cached locally can still read legacy pages.
  • Gating is envelope-level: it tells the shell what to do; the shell must cooperate.
  • No reverse-proxy integration yet (nginx/Cloudflare rules are a Batch 13+ step).
  • stub_html is intentionally minimal — not styled via the shell so it still renders if shell assets are unreachable.
  • No audit log of gating decisions.
  • No caching of gate responses — every page load calls the endpoint.

Notes