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.

Built
Written
Author
Okavyx
Status
Built
Audience
Technical owners and anyone curious how it was built

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.

0passwords for people
0ports open to the internet
1identity provider
~12apps on the shared base
2,300+commits since February 2026

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.

Design principle. Security properties are removed by deletion, never by configuration. An old login path, a legacy sign-in method or a shared-secret mode is deleted from the code, not switched off behind a flag. A flag that fails open looks identical to no control at all, and deleted code cannot regress.

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.

Browserpasskey device CloudflareTLS, WAF, tunnel One Linux server, one Compose project Caddythe only door App containers Portal (sessions) Identity provider Postgresno internet route
Figure 1. Every request takes the same path. The tunnel is established from the server outward, so the server accepts no inbound connections.
Shared pieceWhat it gives every app
EdgeOne reverse proxy that terminates every hostname, applies one set of security headers and is the only route to any container.
IdentityOne passkey-only identity provider. Nothing else authenticates anybody.
SessionOne signed session token minted in one place and verified everywhere with a public key.
DatabaseOne Postgres instance, one least-privilege login role per service.
DeployOne pull-and-rebuild procedure with a committed probe in front of it.
ContractA 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.

Apps with their own code (OIDC) Visitor Portal Identity providerpasskey check Session tokensigned, short-lived Portal is an OIDC client: it proves who the person is with the provider, then issues the session. It decides nobody's identity itself. Code-less or static sites (forward auth) Visitor Caddy Identity providerallow or deny The site Each gated site has its own provider, so a session earned on one site cannot open another.
Figure 2. Two ways onto the same front door. Neither lets an app check passwords or hold users.

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:

General service network (ingress from the edge) Edge Apps Portal Data zone: no internet route Postgres
Figure 3. Isolation by construction: the database cannot reach the internet, and the identity provider sits on its own private paths.
  • 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.

TierRule
1. Source of truthA dedicated secret store, separate from the servers, holds every secret, named by project, service and credential type.
2. Delivery to a containerSecrets 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 configurationA gitignored file holds non-secret runtime settings only, with a shrinking residue still to migrate.
4. Source codeNothing. 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.

Branch + PR Probe + guardrail Reviewthen merge Mergeprobe and review passed Pull + rebuildone service Live checkhealth + probe
Figure 4. Every change travels the same road. Production is always the main branch and nothing else.

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

WhenWhat changed
February 2026First commit: a Docker-first platform with a shared header, account pages and status page. Early apps bring their own login.
March 2026Busiest month of building: the shared services and the first set of apps appear.
Mid 2026The 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 2026Authentication 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 2026Backup 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.

← All white papers Log a job →