SECTION 01
Keep the topology deliberately small
A browser reaches one Next.js service; that service reaches PostgreSQL over the Compose network; only the web port is published to the host LAN.
The production image compiles a standalone Next.js application and starts it as an unprivileged Node process. Docker Compose waits for PostgreSQL health before starting the web service, injects the database connection only into the server container, and persists the database in a named volume. The browser never connects to PostgreSQL. Server components and route handlers query through the postgres client, while client components call same-origin JSON endpoints for mutations and interactive state.
That separation is more important than the number of containers. Curriculum pages can be server rendered from the published release, assessment handlers can create and grade sessions transactionally, and browser-only affordances such as narration or device preferences stay on the client. The topology has one state authority—the database—so a learner can continue the shared campaign from another device on the same LAN without reconciling browser caches.
- LAN browser
Receives HTML and runs explicit client components.
- Next.js web
Standalone Node server, pages, and route handlers.
- PostgreSQL
Published curriculum and shared learner state.
- Named volume
Durable database files across container replacement.
SECTION 02
Place each capability on the correct side of the boundary
Database credentials, grading keys, and release operations stay in server-only modules; pointer interactions, local preferences, and speech synthesis stay in client components.
The database and session-store modules import server-only, making an accidental client import fail during the build rather than leaking a connection path into a browser bundle. Route handlers validate JSON payloads, check same-origin mutation requests, and translate expected failures into public errors. Interactive components receive already-reduced data and never need the database connection string. This gives the codebase a reviewable boundary even though server and client code share one TypeScript project.
The browser still owns transient presentation state: which lesson section is active, whether narration is paused, which locally installed voice is selected, and which pathway card is expanded. Durable learning state—progress, answers, bookmarks, release IDs, and audit records—returns to PostgreSQL. Keeping those categories distinct prevents a cleared browser profile from erasing learning history and prevents convenience preferences from becoming shared-account policy.
| State | Owner | Reason |
|---|---|---|
| Published release and lesson progress | PostgreSQL | Shared, durable, and transactionally updated. |
| Session item snapshots and grading keys | Server + PostgreSQL | Must not be trusted to the browser before feedback is due. |
| Narration voice and speed | Browser local storage | Device capability and personal preference vary by browser. |
| Pathway preview selection | Browser local storage | A reversible planning preference, not completion evidence. |
SECTION 04
Treat LAN and noindex as context, not controls
The root metadata requests noindex/nofollow, but search directives and private addressing do not authenticate a request or close a network port.
The release advertises a private-network URL and binds the web process to all container interfaces so Docker can publish port 3000. Root metadata sets robots to noindex and nofollow, and sensitive result/admin pages reinforce that policy. Response headers add a restrictive content security policy, deny framing, disable selected device permissions, and require same-origin mutation requests. Together these measures reduce accidental exposure and browser attack surface.
They do not establish access control. A host firewall, segmented network, reverse proxy, TLS policy, or login system is outside this repository. Any device able to reach the published port can reach the learner and admin surfaces under the current model. Therefore the accurate claim is “designed for a trusted LAN and excluded from indexing,” not “secure because it is on a LAN.” Moving the service beyond that boundary requires an explicit security design before it requires marketing copy.
REFERENCES
Sources and further reading
- Server and Client ComponentsNext.js
Framework reference for keeping data access on the server while isolating interactive browser code behind explicit client boundaries.
- Deploying a Next.js applicationNext.js
Framework deployment reference for the standalone Node.js output used by the container image.
- TransactionsPostgreSQL Global Development Group
Database reference for all-or-nothing release changes, session creation, grading, and auditable state transitions.
KEEP EXPLORING