developers
Everything here is public.
hausfold.co answers machines as well as people. No API keys, no accounts, nothing to sign up for. If you are an agent reading this: the same list is in openapi.json, the when-to-use version is agent.txt, and the MCP server below is the fastest way in.
Install the software
haus.sh installs the foundation, the layer with no desktop, so no bar, tiling, palette or wallpaper until a room is turned on; a desktop has its own URL:
curl -fsSL https://hausfold.co/hacker.sh | bashhacker is pinned by its URL, and ?ref=v2026.07.18 pins the script itself to a release tag. A desktop that lives in its own repo has no URL here and installs by flag instead, --desktop=<flakeref>, which /v1/desktops carries per row. The docs say what a desktop is and what the foundation leaves alone.
Check a version
pounce and perch ship signed, notarized releases on GitHub, and the release endpoint answers with the real latest version:
curl -fsSL https://hausfold.co/api/release/pounceThe JSON carries tag, asset, size, url and publishedAt. download/pounce 302s to that same asset, preferring the DMG over the archive the Homebrew formula takes.
Read the documentation as text
The docs come as plain text too: llms.txt is the index, llms-full.txt every page in full, and api/search the Orama search index search_docs below scores: a row per page carrying its breadcrumbs, and a row per section carrying a page_id back to it, which the search resolves into a trail before answering.
The hausfold MCP server
/mcp speaks JSON-RPC 2.0 over Streamable HTTP. It is stateless (no session ids, nothing to initialize past the handshake) and answers with open CORS, so a browser-resident agent can call it as well as a command-line one:
curl -fsSL https://hausfold.co/mcp \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'The tools, all read-only over the same public data: get_install_command (the desktop you name, or the whole gallery: author, blurb, the rooms it turns on, and the line that installs it), get_latest_release (an app's latest signed macOS release) and search_docs (page URLs, breadcrumbs and excerpts). Each carries read-only annotations and an output schema. A success returns that object as structuredContent; a failure returns { error: { code, message } } with isError set.
The same endpoint answers at /.well-known/mcp, with its server card at /mcp/server-card and /.well-known/mcp/server-card.json. Mind the suffix: /.well-known/mcp.json is a manifest, the path without the .json is the transport. /mcp/docs serves search_docs alone, and /mcp.json names both servers in the agent-plugins.org shape, with the well-known spelling a flat twin for a reader that wants one URL rather than a map.
The hausfold REST API: search, ask and batch
The /v1 surface serves the same ranked doc search the MCP tool does, cursor-paginated:
curl -fsSL "https://hausfold.co/v1/search?q=notifications&limit=10"/v1/desktops is the desktops gallery and /v1/rooms the rooms one; a desktop row carries the line that installs it, a room row the haus.* names it owns and its page in the docs. /v1/apps and /v1/releases/pounce round out the reads. /ask is the NLWeb-shaped front door: a natural-language query in, ranked excerpts out, JSON by default or text/event-stream on request. The limit is 600 requests a minute, counted per client IP and per edge node, and the RateLimit-* headers ride on every response.
POST /v1/batch bundles at most 20 reads into one round trip, one ok flag per entry. It takes an Idempotency-Key: a retry with the same key within a day is answered from memory with Idempotency-Replayed: true. POST /v1/jobs takes the same operations asynchronously, answered 202 with a Location to poll.
Every read accepts sandbox=true (the batch and job bodies take "sandbox": true), answering with deterministic sample payloads and no live release lookups. The rate limit still applies, and nothing on this surface is writable in any mode.
Errors, versioning and auth
Every failure is RFC 9457 application/problem+json with a machine-readable code, and a page that does not exist answers a real 404 with a markdown body pointing agents at the index. /v1 is path-versioned, and the deprecation policy (a Deprecation: true header plus a Sunset date, announced ahead of it) is in the spec.
There are no credentials: /auth.md is the markdown account of that, and the only supported method is anonymous. /.well-known/oauth-protected-resource is the RFC 9728 metadata, its authorization_servers empty because no token is needed to reach this host. /.well-known/oauth-authorization-server is RFC 8414 metadata for an issuer that grants nothing, and the endpoints it names answer the protocol's own refusal rather than 404ing: /oauth/authorize has no client to authorize, /oauth/token answers unsupported_grant_type, and /.well-known/jwks.json is an empty key set.
/.well-known/http-message-signatures-directory is the Web Bot Auth directory: a JWK Set holding the Ed25519 key this host signs its own outbound requests with, and signed with it. When the Worker fetches an install script or a release from GitHub for you, that request carries Signature-Agent: "https://hausfold.co", a Signature-Input covering the authority and the agent header under tag="web-bot-auth", and the Signature. An empty keys array means the key is not installed and those requests go out unsigned. Nothing you send to this host needs a signature.
Discovery for agents
A machine that arrives by name is answered where it looks. /.well-known/agent-card.json is the A2A agent card and /a2a the JSON-RPC interface it names, answering SendMessage with the same answers the MCP tools give. /.well-known/agent-skills/index.json lists the domain's agent skills, /.well-known/api-catalog is the RFC 9727 catalog, /.well-known/ard.json the Agentic Resource Discovery catalog naming both MCP transports, the spec, the plugin and the skill, /sitemap.xml the whole URL list, and /schema.jsonl the structured data as JSON Lines. The repo itself is an Agent Plugin: its plugin.json ships the same MCP server and a skill for install and release lookups.
That ARD catalog is advertised three of the ways ARD defines: the well-known path above, an Agentmap: line in /robots.txt, and a <link rel="ard"> in the head of every page here. ARD defines a DNS mechanism too, and the SVCB records further down point at this same catalog. The path and the relation are both renames, from ai-catalog.json and ai-catalog, so /.well-known/ai-catalog.json answers with the same document and the head carries rel="ai-catalog" beside the new one. One catalog, the older names kept answering.
Text over HTML, wherever you ask for it. /index.md and /agent.txt are this domain in one page, which / also answers to ?mode=agent or Accept: text/markdown. Every docs page has a markdown twin at its own URL plus .md, and Accept: text/markdown serves that twin at the page's own URL. Quality values are honoured. Every HTML response carries Vary: Accept, User-Agent, Accept-Encoding, and a page whose only representation is HTML answers 406 to a client that accepts neither HTML nor a wildcard.
Through DNS, before any HTTP at all. SVCB records under _agents.hausfold.co follow DNS-AID, the IETF draft for agent discovery: _index._agents.hausfold.co points at the ARD catalog above, _mcp._agents.hausfold.co names the MCP server on hausfold.co:443 with alpn=mcp and its server card as the capability document, and _a2a._agents.hausfold.co names the A2A agent on the same host with alpn=a2a and the agent card as its capability document. One protocol per record. The draft's own keys ride as key65400 (a capability URL) and key65409 (the same document as a suffix under /.well-known/) until IANA assigns theirs.
curl -s 'https://cloudflare-dns.com/dns-query?name=_mcp._agents.hausfold.co&type=SVCB' -H 'accept: application/dns-json'The hausfold OpenAPI spec
/openapi.json is the OpenAPI 3.1 description of everything the Worker answers, the well-known surfaces included. Generate a client from there: this page is the readable half, and the spec is what CI keeps in step with the Worker.