Technical white paper · v1.0
Okavyx Labs: one front door, one edge, one deploy for a dozen apps
How a dozen web applications share one Linux server with passkey-only sign-in, a single reverse proxy, a locked-down database, and security written down as rules a machine can check.
01Executive summary
Okavyx Labs is the shared layer underneath a dozen small applications built and run by Okavyx. It was started in February 2026 as a Docker-first platform on a single Linux server, and it has since been rebuilt around one idea: every decision that is the same for every app (who you are, where secrets live, what the web headers are, how code ships) is made once, written down, and enforced.
It is a demonstration of capability. It describes how the system works and why, in principles. It deliberately leaves out names, addresses and settings that would help someone attack a live system.
02The problem
Before the platform, each app was built on its own. Each had its own login page, its own idea of a session, its own database password and its own interpretation of a security header. The result was four hand-built sign-in pages across four repositories, an estate nobody could describe in one sitting, and three occasions on which administrators were locked out of their own systems.
The platform does not exist to host apps; a server can do that without help. It exists to cut the cost of the next app. Adding the twelfth service should not mean deciding again how login works, where a secret lives, or what the content security policy is.
03Architecture
Everything runs as one Docker Compose project on one Linux server. Four things are shared and everything else is a tenant: the edge, the identity provider, the database and the written contract.
| Shared piece | What it gives every app |
|---|---|
| Edge | One reverse proxy that terminates every hostname, applies one set of security headers and is the only route to any container. |
| Identity | One passkey-only identity provider. Nothing else authenticates anybody. |
| Session | One signed session token minted in one place and verified everywhere with a public key. |
| Database | One Postgres instance, one least-privilege login role per service. |
| Deploy | One pull-and-rebuild procedure with a committed probe in front of it. |
| Contract | A written service contract, a security standard, and a library that makes following them the easy path. |
04The edge: tunnel and Caddy
Traffic reaches the server only through a Cloudflare tunnel that the server itself opens outward. No inbound port is forwarded, so there is nothing on the server for an internet scanner to find. If the tunnel is down, nothing is reachable at all, and that is an accepted trade: there is no second, forgotten path.
Caddy sits behind the tunnel and routes by hostname to the right container over an internal network. It does three jobs for every app:
- One header policy. Transport security, framing, content-type and referrer headers are applied in one place rather than app by app.
- Explicit 404s. Retired hostnames answer with a plain 404 instead of falling through to something else.
- Forward authentication. For static or code-less sites, Caddy asks the identity provider before serving a single byte (section 5).
05One sign-in: passkeys
An open-source identity provider runs on the same server. It is the single front door. People sign in with a passkey: a credential held on their own device and unlocked with Face ID, a fingerprint or the screen lock. There is no password to steal, reuse or phish, and no username box.
New people are added by invitation only. There is no self-service sign-up. Each app is bound to one group, and membership of that group is the whole access rule. Apps never keep their own user list and never second-guess the provider's decision; every time one did, the owner ended up locked out.
Principles behind the sign-in
- One front door, no spare. Adding a fallback identity provider for resilience was rejected: a second door that is merely switched off is still a door. Google sign-in was removed, not disabled.
- Separate clients per app. Apps never share an OIDC client. Otherwise a token issued for one app could be accepted by another.
- A stable identity key. The user identifier that links a person to their platform record is stable and taken only from the signed token, never from display fields.
- The sign-in hostname never changes. Passkeys are bound to the site name; changing it would silently destroy every registered passkey.
- Ownership is narrow. Only one working context may change identity configuration. Every other part of the system asks it, rather than editing wiring that looks like ordinary config.
- Drift is checked nightly. A scheduled job compares the live identity configuration with an approved baseline and runs a set of live safety checks.
06Sessions and authorisation
After sign-in, the Portal issues one signed session token. Every app verifies it with a public key. Apps can check a token; none of them can mint one, so compromising an app does not let an attacker forge a session for another.
Authorisation is answered in three separate layers, because they answer different questions: who are you (the identity provider), are you allowed on the platform at all (a short list the Portal holds), and may you open this app (an app list inside the token). Sessions are short, and signing out of any app ends the platform-wide session.
The old symmetric shared-secret session mode was deleted outright once the asymmetric one worked, for the reason in section 2.
07Data and network isolation
Containers sit on purpose-built internal networks rather than one flat one. The pattern, not the layout, is the point:
- One login role per service. Each role is not a superuser, cannot create roles, and owns only its own tables. A new role cannot even connect until it is explicitly granted access.
- Outbound traffic is controlled rather than left open.
- Known weaknesses are recorded so nobody assumes a control exists that does not.
08How secrets are handled
Four tiers, and the order is the policy.
| Tier | Rule |
|---|---|
| 1. Source of truth | A dedicated secret store, separate from the servers, holds every secret, named by project, service and credential type. |
| 2. Delivery to a container | Secrets arrive as read-only files mounted into the container, not as environment variables. That keeps them out of process listings, crash reports and any log that prints the environment. |
| 3. Plain configuration | A gitignored file holds non-secret runtime settings only, with a shrinking residue still to migrate. |
| 4. Source code | Nothing. Ever. Secrets are never in repositories, logs, chat or screenshots. |
Database connection strings in configuration carry a role name and no password. Secrets are moved between machines by single-use encrypted links, never by pasting.
09Deploy and probe
The deploy procedure is deliberately small: pull the main branch on the server, rebuild the one service that changed, bring that service up. Servers hold read-only deploy credentials, so nothing can be pushed from production back into the repository.
Probes: done means it can fail
A change is not finished because it looks right. It is finished when a committed probe, a small script that exits with an error when the thing is broken, has been watched failing once and then passing. A probe that has never been seen to fail proves nothing.
- Before every push a local hook runs the repository probe; a failure blocks the push.
- On every pull request a deterministic security guardrail runs in CI and fails any change that introduces a new violation of the written security standard. A second, model-driven review adds judgement calls but is advisory, so the hard gate never depends on an AI being available.
- On the live system probes check the substrate itself, and an external monitor runs each service's checks continuously.
- Specifications are stamped with the commit they were verified against and the method used. A document updated without the verification behind it is flagged as exactly the failure the stamp exists to catch.
Review and ownership
Every change is reviewed independently and passes committed probes before it ships. Each repository has one clear owner at a time, and changes to sign-in, data deletion or spending need explicit, typed approval.
10Backup and recovery
The nightly archive holds every database and every secret, so it is treated as the most sensitive thing the estate produces.
- Encrypted before it leaves the server, to a public key. The server cannot read back what it wrote, and neither can the remote store; only a private key held away from the server can decrypt.
- Least privilege for shipping. The credential used to ship a backup can add one and do nothing else.
- Verified, not trusted. The script confirms the artefact is really encrypted, records a checksum, and compares it with the remote copy.
- Restored in drills. A backup is a hypothesis until restored. The database half and the file-storage half have each been restored into scratch environments, with record-by-record reconciliation and a deliberate negative control, and the report states exactly what remains unproven.
Recovery is planned, not improvised. With one front door, an identity outage locks everyone out, and the temptation is to add a local login "temporarily". Instead there is a documented, tested recovery path that does not add a standing login.
11Timeline
| When | What changed |
|---|---|
| February 2026 | First commit: a Docker-first platform with a shared header, account pages and status page. Early apps bring their own login. |
| March 2026 | Busiest month of building: the shared services and the first set of apps appear. |
| Mid 2026 | The substrate gets a written contract, a security standard with numbered rules, and a CI guardrail. Symmetric sessions and extra sign-in routes are deleted. |
| July 2026 | Authentication is moved to a single passkey-only identity provider with its own dedicated ownership, documentation and drift checker. The portal becomes its own labs front page. |
| August to October 2026 | Backup drills, network isolation, retirements of old apps, and every document re-verified against the live system. |
12Lessons
- Solve the twelfth app, not the first. The value of a platform is the decisions you stop re-making.
- One front door beats two. A spare path is a path an attacker also gets.
- Delete, don't disable. Removed code cannot be turned back on by a bad default.
- Measure the live system. Weaknesses and stale documents were found by testing what was running, not by reading what was written.
- A probe you haven't seen fail isn't a probe.
- Write the gaps down. A recorded known weakness is a to-do. An unrecorded one is a surprise.
- Plan the outage you can't avoid. If one component is a single point of failure, write the recovery before you need it.
- AI builds fast and forgets faster. Keep the rules, the contract and the checks in the repository, where the next session will find them.
Our work is always evolving, and so are the AI vendors and their products. Nothing described here is set in concrete. Each paper describes its system as it stood in October 2026, when it was written; the date shown is when the app was first built.