Security model
Sharing one app never shares your network.
What each part of Houseguest™ can do, what it cannot, and what it keeps.
Who can do what
| Party | Can | Cannot |
|---|---|---|
| A viewer | Use the preview's hostnames until it ends or they are revoked | Use a forwarded link without the invited inbox; reach names outside the preview |
| The agent | Reach the hostnames on its own allow-list | Accept a name its host did not allow |
| The edge | See requests and responses of an open preview, to check each one | Reach any name outside the agent's allow-list; sign in to your app without a user's credentials |
| A compromised edge | Send requests to allowed names and TCP services at any time | Reach the rest of your network; widen the allow-list |
| You, on the self-hosted edition (Enterprise) | Everything, on servers you run | — no preview traffic or workspace data reaches Houseguest. The hosts that serve releases and the agent image (GitHub's container registry) see the server's IP address when it downloads them |
The agent
- It opens one connection out to the edge on port 443. Nothing connects in, so no firewall rule changes.
- It joins once with a token that works one time within an hour, and registers its own Ed25519 key. Each later connection is signed with that key over the edge's name, the time and a one-time value, so a captured connection cannot be replayed.
- Its allow-list, HG_ALLOW, is set on its own host. It may be exact names, or one level under a domain such as *.lab.example.com. The console can turn names on within that ceiling and never past it, and the agent can be told to ignore the console entirely.
- It connects to the app with TLS, sends the real hostname and checks the app's certificate, as a browser on your network would: against public roots, plus your internal CA if you name one. It removes Houseguest's own cookie before forwarding.
- It runs as an unprivileged user: in a container of about 10 MB with no shell, or, without Docker, as a locked-down systemd service.
- On the hosted service, a name is served on its own hostname only under a domain your account has verified with a DNS TXT record. Another account's agent cannot claim it.
Viewers
- Invites work once. Opening the link shows a confirmation page, so link previewers in chat and mail cannot use it up.
- Email codes confirm every sign-in with standing access, and every invite unless the sharer turns them off. They work for 10 minutes and allow 5 wrong tries. A new code can be sent after 30 seconds, up to 5 an hour.
- Standing access sign-in pages answer every address the same way, so the page reveals nothing about who is on the list. Code emails are capped per address and per network.
- Sessions use a cookie marked Secure and HttpOnly, scoped to the preview's own domain, lasting until the preview ends or a set number of days. Revoking a person or ending a preview stops web access at the next request. TCP and terminal connections already open run until they close.
Requests without a session
Some apps make requests without cookies: site icons, health checks, or API calls with a bearer token. The gate cannot tie those to a person, so it refuses them by default. Three narrow exceptions, each visible in the request log:
- Paths you list as open, such as a health check.
- Site icons, from the network address a signed-in viewer used in the last 15 minutes.
- Optionally, per preview, other cookie-less requests from that same address, rate-limited. People behind one office network share an address, so this stays off unless an app needs it.
Houseguest addresses
On the hosted service, a preview can use addresses such as app.acme.houseguest.page instead of the app's own hostnames.
- The app's internal names never appear in public DNS or certificate logs. Each account has one wildcard certificate for its addresses, so certificate logs show the account's name, not its addresses.
- The edge sends each request to the agent under the internal name, and rewrites the address in Origin, Referer, Location, CORS, Content-Security-Policy and cookie-domain headers. It never rewrites page content.
- Each account's addresses sit under its own name, and one sign-in covers them. Until houseguest.page is on the Public Suffix List, a page under another account's address could set a cookie for all of houseguest.page. Houseguest's sign-in ignores such cookies; the app's own cookies do not get that protection until the listing.
SSH and other TCP services
- The agent's host lists each service by exact name and port. The console cannot add to that list.
- With the houseguest command line, the edge relays bytes without interpreting them. Encrypted protocols such as SSH stay encrypted end to end; a protocol without encryption, such as plain HTTP or an unencrypted database connection, is readable at the edge.
- The browser terminal runs the SSH session on the edge, which sees it in the clear, as it sees web previews. The server's host key is remembered on first use, and a changed key stops the terminal.
- Connections are checked when they start. Revoking a person or ending a preview stops new connections; open ones run until they close.
The console
- People who share previews sign in with an emailed code, a passkey, or Sign in with Microsoft. There are no Houseguest passwords to leak or reuse.
- A second step, a passkey or an authenticator app, can be required for Owners and Admins or for everyone. On the hosted service it is required for Owners and Admins by default. Ten single-use recovery codes cover a lost device.
- Members can also sign in through the workspace's own identity provider with single sign-on (OIDC or SAML), which the workspace can require. It is part of the Business plan and included in early access.
- Roles decide who does what: Owner, Admin, Member, Billing and Auditor, or custom roles, granted for the whole account, a group of previews or one preview. Members run only the previews they opened. Nobody can grant more than they hold.
- Every change is checked for a per-session token and the page's origin, and the console refuses to be framed by other sites. Every change, including refused ones, is recorded in the activity log with the person, the time and the IP address.
What is kept
- Invite links, codes, session tokens and command-line tokens are stored only as hashes.
- The request log keeps the time, the person, the method, the hostname, the path, the result, the browser family and OS, the device type and the country, for 90 days. Never query strings, never request or response bodies.
- The edge's web-server log for previewed hostnames records the client IP address, the request line without its query string, and request headers such as the user agent. It rotates by size.
- The activity log keeps every console change with the person, the time and the IP address, for one year.
- On the hosted service, email goes out through Amazon SES, with Resend as the fallback. Mail to Microsoft consumer mailboxes (Outlook.com, Hotmail, Live and MSN) goes through Resend first. Open and click tracking are off on both, so links in Houseguest's email go straight to your app's address. The self-hosted edition sends through your own mail provider.
Where the edge runs
To check each request, the edge has to read it, so it terminates TLS. Houseguest is a hosted service, so that edge is Houseguest's. If no third party may see your traffic, the self-hosted edition, on the Enterprise plan, runs the edge on a Linux server you control: the same software, and no preview traffic or workspace data reaches Houseguest. The installation materials come from Houseguest with the plan; the hosts that serve releases and the agent image see the server's IP address when it downloads them.
Reporting abuse
Previews are created by customers. Phishing, malware, copyright infringement and other misuse on a preview: how to report abuse, or email [email protected]. A confirmed report suspends the customer's workspace, and only that workspace.
Security reports: [email protected]. Other questions: [email protected].