The Security Boundary Is Larger Than the Login Screen

For openGym, security begins with the login but does not end there. This chapter traces what a passkey proves, how that proof becomes a session, and which checks protect a later state-changing request. It then follows the remaining failure surfaces—origins, proxies, passwords, media, push, backups, administrators, and the host filesystem—so a reader can distinguish application-level protection from the trust that self-hosting still places on the operator.

What the Application Proves at Sign-In

A passkey proves a relationship to a particular relying-party identity, not a general claim that every request from a device is safe. openGym asks the server to verify WebAuthn registration and authentication against the configured browser origin and relying-party hostname. That exact binding is why the public hostname matters. A passkey created for one hostname is not a portable password that can be replayed against another address.

After the WebAuthn ceremony, openGym uses a signed session value. The session carries the profile identity, an expiry, and a per-account version. The server verifies the signature and checks that the account is still enabled and that the session version is current. When the user chooses “sign out everywhere,” the account version changes, so previously issued sessions stop verifying without requiring every device to be located individually. A paired phone uses the same kind of signed value in an authorization header because its WebView does not share the browser’s cookie context. The transport differs, but the server still checks the same underlying identity and expiry rules.

That session is not itself proof that every action is safe. For browser requests that change state, openGym also checks that the request came from the expected application context using browser fetch metadata or an origin fallback. This matters because a cookie can be attached by the browser without the user consciously choosing the request. A hostile page should not be able to make a signed-in browser submit a profile-changing operation merely because both pages are reachable under related web infrastructure.

The profile identity then scopes the data route. A normal user does not select another person’s state file by naming it in a request. The server derives the profile from the authenticated session and reads or writes the corresponding state. That is a different control from authentication: authentication answers who is making the request, while authorization answers which profile that identity may reach.

Multiple passkeys and device links solve another transition. A person may need to sign in from a phone, laptop, or security key without creating a second empty profile. openGym allows more than one passkey and uses a one-time device code to let another device create its own credential. The code is short-lived, stored as a hash, and consumed when used. A profile cannot remove its last remaining way in. These rules protect the durable account boundary: adding a new way in is a lasting capability, so it needs stronger proof than simply presenting an already open session.

Optional password sign-in changes the threat model rather than replacing it transparently. Passwords can be guessed, reused, phished, or attacked against a stolen database hash, so the feature is off by default and uses scrypt plus in-memory throttling when enabled. The throttle limits password routes, but it is not a universal shield for every endpoint or every deployment.