tinfoil-proxy

command module
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 26, 2026 License: AGPL-3.0 Imports: 15 Imported by: 0

README

Tinfoil Proxy

A verified local HTTP proxy to a Tinfoil secure enclave. The Go binary opens http://127.0.0.1:3301/v1, verifies the upstream enclave against the public attestation transparency log, pins the attested public key, and forwards OpenAI-compatible traffic. An Electron menu-bar wrapper (Tinfoil Proxy.app) is built from the same repo and supervises the same binary, exposing start/stop, port, and live verification status from the status bar.

Install the standalone CLI:

go install github.com/tinfoilsh/tinfoil-proxy@latest
tinfoil-proxy --port 3301

Or grab a pre-built CLI binary or the menu-bar app installer from the releases page.

This is the canonical home for the proxy server. The legacy tinfoil proxy subcommand in tinfoilsh/tinfoil-cli is deprecated.

Layout

main.go         CLI entry point (cobra flags, defaults, error wiring)
proxy.go        Reverse proxy + verified-client wiring + handshake protocol
go.mod / go.sum Go module + locked dependency graph
src/
  main/         Electron main process (proxy lifecycle, IPC, menu, popup, updater)
  preload/      contextBridge exposing a typed window.tinfoil API to the renderer
  renderer/     React popup UI + embedded verification-center iframe
assets/         App and tray-status icons (PNG + macOS .icns)
build/          Code-signing entitlements, .pkg postinstall, iconset source
scripts/        build-cli.sh — compiles the proxy from the local module
resources/bin/  (gitignored) The tinfoil-proxy binary copied in at build time

How the bundled proxy works

scripts/build-cli.sh cross-compiles the proxy from the in-repo Go source for the target OS/arch (universal binary on macOS), writes it to resources/bin/tinfoil-proxy, and electron-builder ships it inside the installer. There's no fetching from a separate repo — the Go and Electron sources live side by side and are released together.

Handshake protocol

When the Electron app launches the bundled binary it passes a hidden --handshake flag. The Go child then runs attestation, binds its listener, writes a single JSON line to stdout ({"event":"ready","enclave":"...","repo":"...","listen":"..."}), and waits on stdin for go\n or abort\n. The parent independently re-verifies the announced enclave and only sends go when the JS-side verification matches. Without --handshake (the standalone CLI case) the binary serves immediately, just like before.

Requirements

  • Node.js 20+
  • Go 1.25+
  • macOS 13+ recommended for development; Linux / Windows build paths exist but are less exercised locally

Development

npm install
npm run dev

npm run dev first compiles the tinfoil-proxy binary into resources/bin/, then launches the Electron app with hot-reload for the renderer.

Useful scripts
Script Purpose
npm run dev Build the proxy for the host OS and start Electron with hot-reload
npm run build Production build of main + preload + renderer into out/
npm run lint ESLint with --max-warnings 0
npm run typecheck TypeScript projects for both Node and Web
npm run build:cli Just rebuild the embedded tinfoil-proxy binary
npm run package:mac:x64 Build macOS installers for Intel (.pkg, .dmg, .zip)
npm run package:mac:arm64 Build macOS installers for Apple silicon
npm run package:linux Build Linux installers (.AppImage, .deb)
npm run package:win Build Windows installer (.exe, NSIS)

The macOS builds run per-architecture to avoid an electron-builder race where the .pkg builder removes a shared distribution.xml file. CI runs them as two parallel macOS jobs.

Packaging for distribution

electron-builder reads electron-builder.yml. The macOS configuration enables Apple's Hardened Runtime, ships entitlements for V8 JIT and dynamic library loading, and runs notarization at build time.

To produce a signed + notarized macOS release locally:

export CSC_LINK="$(base64 -i /path/to/DeveloperIDApplication.p12)"
export CSC_KEY_PASSWORD='the export password'
export CSC_INSTALLER_LINK="$(base64 -i /path/to/DeveloperIDInstaller.p12)"
export CSC_INSTALLER_KEY_PASSWORD='the export password'
export APPLE_ID="releases@example.com"
export APPLE_APP_SPECIFIC_PASSWORD="abcd-efgh-ijkl-mnop"
export APPLE_TEAM_ID="ABCDE12345"

npm run package:mac:arm64

If those env vars are absent, set CSC_IDENTITY_AUTO_DISCOVERY=false to produce an unsigned build for smoke testing.

App bundle layout (macOS)
/Applications/Tinfoil.app/
  Contents/
    MacOS/Tinfoil                 # Electron host
    Resources/
      bin/tinfoil-proxy           # Universal (x64 + arm64) proxy binary
      app.asar                    # Renderer + main process bundle
      assets/icon-tray-*.png      # Menu-bar template icons

The .pkg installer drops the app into /Applications and runs build/pkg-scripts/postinstall, which opens the app once so the tray shows up immediately.

CI

Three GitHub Actions workflows live in .github/workflows/:

  • test.yml — runs on every PR / push to main; installs deps, lints, typechecks, and produces an unsigned production build on Ubuntu.
  • release.yml — runs on tag pushes (v*); builds, signs, and publishes installers for macOS (x64 + arm64) / Linux / Windows to the GitHub release matching the tag, then cross-compiles the standalone tinfoil-proxy binary for darwin/linux/windows (amd64 + arm64 where applicable) and uploads those alongside the Electron installers.
  • zizmor.yml — audits all workflow files for common GitHub Actions security issues whenever a workflow changes.

The release workflow expects these repository secrets (only used by the macOS jobs):

Secret Purpose
APPLE_CERTIFICATE_P12_BASE64 Developer ID Application cert + key, base64 (base64 -i cert.p12 | tr -d '\n')
APPLE_CERTIFICATE_P12_PASSWORD Export password for the Application .p12
APPLE_INSTALLER_P12_BASE64 Developer ID Installer cert + key, base64
APPLE_INSTALLER_P12_PASSWORD Export password for the Installer .p12
APPLE_ID Apple ID email used for notarization
APPLE_APP_SPECIFIC_PASSWORD App-specific password for notarytool
APPLE_TEAM_ID 10-character Apple developer team ID

GH_TOKEN is wired automatically from secrets.GITHUB_TOKEN. Each .p12 must contain both the certificate and its private key — when exporting from Keychain Access, select the certificate row (not the indented private-key row) and use a real export password.

Auto-updates

The packaged app polls GitHub Releases every 6 hours via electron-updater and prompts the user when a new build is available. .deb installs do not receive auto-updates; users on that target must reinstall the latest .deb manually. AppImage and macOS .zip / .dmg update in-place.

Security notes

  • The renderer runs sandboxed: contextIsolation: true, sandbox: true, nodeIntegration: false. All renderer → main calls go through a typed preload bridge.
  • The popup HTML ships a strict Content-Security-Policy header that only allows the verification-center iframe to load from https://verification-center.tinfoil.sh.
  • The iframe itself is restricted with sandbox="allow-scripts allow-same-origin" and a no-referrer policy.
  • The macOS app runs under Hardened Runtime with notarization enabled by default; unsigned builds are only produced when CSC_IDENTITY_AUTO_DISCOVERY=false is set.

Releasing

  1. Bump "version" in package.json.
  2. Commit, push, open a PR, merge to main.
  3. From main: git tag v0.X.Y && git push origin v0.X.Y.
  4. The release workflow builds and uploads installers; the resulting GitHub Release is what electron-updater consumes for auto-updates.

Documentation

The Go Gopher

There is no documentation for this package.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL