Engineering plans / Landed in 1.5 · September 27, 2026

1.5 beta: IPC hardening (XPC, gRPC, llama HTTP, Postgres)

Status at the 1.5 release. Every work item below has landed: Postgres, the gRPC facade and the llama HTTP API listen on Unix sockets in s/ at the root of the App Group container, every XPC listener requires a same-team code signature, the in-app Python reaches llama.cpp over NSXPC, the config-changing RPCs answer only on the socket, and the MCP HTTP server is opt-in. The current picture is in the privacy guide and CLAUDE.md; the text below is the plan as written, line numbers included.

Findings from reading main on 2026-09-25, carried onto v1.5-beta on 2026-09-27. The per-launch GARAGE_GRPC_TOKEN from #104 is on this branch and covers the unauthenticated-port part of item 1. Since then the App Store build hosts GarageIngestXPCService and GarageXPCService in the app’s own process behind anonymous listeners; item 2 covers those listeners too. Line numbers below are from main at the time.

Current state

Work items

  1. Unix domain sockets for gRPC and llama HTTP. Put them in a 0700 directory inside the App Group container.
    • grpc Python takes unix: addresses, grpc-swift/NIO can connect to a Unix socket, NWListener can listen on NWEndpoint.unix, and httpx has HTTPTransport(uds=).
    • On connect, check the peer: read LOCAL_PEERTOKEN and validate its code signature.
    • Teach net/egress.py and the CALLERS test about UDS destinations.
    • The launchers keep working because they carry the group entitlement.
    • This replaces or backs up the #104 token.
  2. Done in this branch: a code-signing requirement on every XPC listener (macOS 13+). Pin team DWVXMLB45Y and the me.rickmark.garage-rag.* identifiers, with a looser requirement for locally signed configurations. Enable hardened runtime and library validation on the app itself.
  3. Inference over NSXPC for in-app Python. Extend the LlamaModelLoaderBridge pattern: a @convention(c) entry point called through ctypes, which releases the GIL.
    • Signature: llama_call(route, json_in, &json_out, &err_out), plus a free function. Swift allocates the reply because embedding batches are large.
    • It forwards through LlamaClient, and LlamaXPCClient prefers it when it is installed.
    • Covers GarageXPCService, the embed worker and GarageMCPServerService. The launchers and a venv garage still need HTTP/UDS, so HTTP becomes opt-in rather than gone.
  4. Gate config-changing RPCs behind the peer check. Changing embedding.ollama_host through SetSetting widens the egress allowlist, which would let document and code chunks go off-box.
  5. Postgres over the same kind of socket. Today the bundled cluster uses TCP only.
    • Current settings: listen_addresses=localhost, unix_socket_directories= (sockets off), --auth=scram-sha-256 (PostgresService.swift:396,460).
    • The protection now is the password in the App Group keychain, not the transport. Any local user can reach port 14824 and try the password.
    • Change: set unix_socket_directories to a directory in the group container, unix_socket_permissions=0700 and listen_addresses=''.
    • Keep local ... scram-sha-256 in pg_hba.conf. peer isn’t worth adding, since every process runs as the same user.
    • libpq/psycopg take the socket through host=/path.
    • The launchers’ “is anything listening on 14824” check becomes “does the socket exist and answer”.
    • Watch out: macOS limits a socket path to 104 bytes (sun_path). .../Group Containers/DWVXMLB45Y.group.me.rickmark.garage-rag/Library/Application Support/GarageApp/... plus .s.PGSQL.14824 is too long. Put all three sockets in a short directory right under the container root (e.g. <container>/s/). Postgres refuses to start when the path is too long, so test with a long username.
    • Check that the sandboxed XPC services can connect() to a Unix socket in the group container. I believe the group entitlement covers it, but I haven’t verified that.
    • Development Homebrew servers and CI keep TCP. This applies only to the app’s own cluster.
  6. MCP HTTP off by default. With Postgres, gRPC and llama on Unix sockets, the MCP HTTP server (127.0.0.1:8787) was the last TCP listener. Assistants are now registered over stdio with the bundled garage-mcp launcher, and the HTTP server runs only after the user starts it on the MCP page (garage.mcp.httpEnabled). An existing install with an HTTP registration keeps HTTP on.