Skip to content

Private Instance Mode

Any Glassy instance can be locked behind sign-in. When Instance Access is set to Private, anonymous visitors never see the landing page, the marketing pages, public author windows, or RSS feeds — they are routed straight to the sign-in screen, and the link they originally clicked is remembered for after they authenticate.

This is the recommended posture for personal appliances, family servers, single-tenant work installs, and any environment where the public web should have zero footprint. A self-hosted appliance is forced into private mode at every boot and cannot be switched out of it.

Private means 'not shared with the world' — not 'invisible to you'

Since v2.36.0-beta.36, a signed-in owner can view their own Public Window on a private instance. Before that, the privacy gate blocked the owner too, which made "private" mean "nobody can see this, including the person it belongs to". Anonymous visitors are unaffected: they still get the sign-in redirect, and the server-side gate is byte-identical. See Public Window.


What changes when Instance Access is Private

Surface Public mode Private mode
GET / Marketing landing Redirect to sign-in
#/, #/about, #/security, #/guide, #/extension, #/neurodiversity, #/obsidian, #/writers, #/students, #/download Marketing pages Redirected to #/login, with the deep link stashed
#/w/:slug (public author windows) Author profile Redirected to #/login
GET /api/window/:slug and subpaths 200 OK 401 { error: 'members_only', code: 'PRIVATE_INSTANCE' }
GET /api/window/:slug/feed/rss RSS XML 401
GET /w/:slug, GET /w/:slug/:noteId (social-preview HTML) Open Graph tags + crawler HTML 401 { error: 'auth_required', code: 'PRIVATE_INSTANCE' }
#/login, #/register, #/forgot-password, #/reset-password, #/login-secret Reachable Reachable — sign-in has to keep working
GET /api/instance { accessMode: 'public', … } { accessMode: 'private', … }

Two details worth knowing if you are testing this yourself:

  • A spoofed crawler User-Agent is not a credential. The social-preview routes are guarded before the User-Agent branch, so announcing yourself as Googlebot does not get you past a private instance.
  • Everything behind a session behaves identically in both modes. Private mode changes what an anonymous visitor can reach, not what a signed-in user can do.

Being redirected to sign-in would be useless if it lost the thing you clicked. It does not.

  1. You follow a link to #/w/alice/some-note on a private instance.
  2. The route is stashed in sessionStorage and the URL is replaced with #/login.
  3. You sign in — password, 2FA, Google, secret key, or a fresh registration all work the same way.
  4. You land on the note you asked for, not on a generic dashboard.

The stash is per-tab (sessionStorage, key glassy:postLoginRedirect), so signing in on one tab does not hijack another, and closing the tab clears it. If nothing was stashed you land on your notes.

Private browsing may drop the deep link

Some browsers disable sessionStorage in private/incognito windows. Glassy treats that as non-fatal — sign-in still works, you just land on the default route instead of the link you clicked. Use a normal window if the deep link matters.


Registration policy is a separate switch

Private mode does not close registration. Whether new sign-ups are accepted is controlled independently by the registration toggle, so all four combinations are legitimate:

Instance access Registration What you get
Private Open Invite-style deployment: anyone with the URL can self-onboard, the public web sees nothing
Private Closed Pure single-user or family install; an admin creates accounts by hand
Public Closed Invitation-only community with public marketing pages
Public Open Fully open instance

On a self-hosted appliance registration is hard-disabled at the server — not merely toggled off — so the first two rows are the only reachable states there.


Turning it on

Self-hosted appliance

Private mode is forced at every boot. There is nothing to configure and no way to turn it off; a change attempting to flip it is silently rejected. This is deliberate: an appliance is one person's data on one machine, and a forgotten setting should not be able to publish it.

To seed a new install as private without touching the admin UI, set INSTANCE_ACCESS_MODE_DEFAULT=private in .env before the first boot.

Cloud, Clear, and other multi-user instances

An administrator toggles Admin → Instance Access (formerly labelled Public Windows: Open / Members Only). No redeploy is needed.

The change takes effect within about 60 seconds, because the setting is cached in-process. Newly opened tabs pick it up immediately — the server injects the current mode into a freshly served, uncached index.html, and the app reads it synchronously on first render, so a private redirect happens before any marketing shell can flash on screen. A tab that is already open holds the previous mode until it reloads.

Flipping to private breaks RSS readers

Any feed reader polling a now-private #/w/:slug/feed/rss will start receiving 401s. There is no per-feed escape hatch by design: if you need a public RSS surface, the instance has to be public. The RSS link is also hidden from the Public Window UI in private mode, because a plain browser navigation cannot attach credentials to it.


Migrating an existing deployment

No action is required. On the first boot after upgrading:

  1. The instance-access setting is backfilled from the older Public Windows field — Members Only becomes private, anything else becomes public — and persisted.
  2. Both fields are kept mirrored on every change, so older admin tooling that reads the legacy field keeps working.
  3. Reads consult the canonical field first and fall back to the legacy one only if it is absent, so the legacy field can be removed later without another migration.

How this relates to INSTANCE_ID

Private mode is one of two independent axes, and it is easy to conflate them:

Mechanism What it decides Who controls it Changeable at runtime?
Instance access mode Whether anonymous visitors see anything Admin toggle (forced private on appliances) Yes, except on an appliance
INSTANCE_ID What kind of Glassy this is (public, clear, self_hosted) Set at deploy time No — requires a redeploy

INSTANCE_ID=self_hosted layers permanent server-side gating on top of private mode: registration is hard-403, the commerce, Stripe and push routes are not mounted at all, transactional email is disabled, error reporting is not initialised, and the owner account is auto-promoted to the lifetime Clear tier on first boot.

For the full matrix of combinations, see Instance Access Modes.


Operational notes

  • GET /api/instance is cached for 30 seconds via Cache-Control. Expect a worst-case 30-second window before every reverse-proxy layer sees a mode change, even though newly opened tabs are immediate.
  • The members_only error string is retained for compatibility. Older clients branch on error: 'members_only'; newer code should branch on code: 'PRIVATE_INSTANCE'. Both are present in the same response.
  • GET /api/instance and the registration-allowance endpoint stay reachable in private mode, because the sign-in screen needs them to render correctly. Neither exposes user data.

Learn more