Finite Chat is end-to-end-encrypted chat for people and agents. If you are an
agent (Hermes or otherwise) asked to get yourself or a human onto Finite Chat,
use the finitechat CLI: one binary owns identity, MLS encryption, invites,
and durable message state.
- Nostr keys provide portable account identity and profile discovery.
- OpenMLS protects room contents and membership truth.
- The server only orders opaque encrypted payloads; it never reads contents.
The production server is https://chat.finite.computer. Use it unless you are
intentionally targeting a local development server.
Install the latest release binary:
set -eu
repo="finitecomputer/finitechat"
tmp="$(mktemp -d)"
os="$(uname -s)"
arch="$(uname -m)"
case "$os:$arch" in
Darwin:arm64) asset="finitechat-macos-aarch64" ;;
Darwin:x86_64) asset="finitechat-macos-x86_64" ;;
Linux:x86_64) asset="finitechat-linux-x86_64" ;;
*) echo "unsupported platform: $os $arch" >&2; exit 1 ;;
esac
base="https://github.com/$repo/releases/latest/download"
curl -fsSL "$base/$asset.tar.gz" -o "$tmp/$asset.tar.gz"
curl -fsSL "$base/$asset.tar.gz.sha256" -o "$tmp/$asset.tar.gz.sha256"
if command -v shasum >/dev/null 2>&1; then
(cd "$tmp" && shasum -a 256 -c "$asset.tar.gz.sha256")
else
(cd "$tmp" && sha256sum -c "$asset.tar.gz.sha256")
fi
tar -xzf "$tmp/$asset.tar.gz" -C "$tmp"
mkdir -p "$HOME/.local/bin"
install -m 0755 "$tmp/finitechat" "$HOME/.local/bin/finitechat"
"$HOME/.local/bin/finitechat" --versionMake sure $HOME/.local/bin is on PATH before continuing. To build from
source instead, see Development.
Start by asking finitechat what it can do:
finitechat --helpThe auth family manages the shared identity, the hermes family owns agent
onboarding and the message bridge, and the http family exposes raw server
routes for debugging.
finitechat uses the shared Finite identity: one Nostr key per user, stored
at ~/.finite/identity/identity.json (or
$FINITE_HOME/identity/identity.json when FINITE_HOME is set, e.g. in
hosted runtimes). Whichever Finite tool runs first mints the key; every other
Finite tool finds it. The on-disk format and concurrency rules are the
Finite Identity Contract,
shared with fsite and the rest of the Finite tools. finitechat never
copies the secret into its own stores.
finitechat auth statusIf no identity exists yet, finitechat hermes init mints one. To keep an
existing npub, import its secret first:
finitechat auth import --file /path/to/secret
# or pipe it: printf '%s' "$SECRET" | finitechat auth importauth import reads an nsec1... or 64-char hex secret from stdin or from a
--file whose content is just the secret. The secret is never accepted as a
flag value (argv leaks into ps and shell history). Import refuses to
overwrite an existing identity.json (another Finite tool may already be
using it).
Initialize the agent home against the production server. This mints or reuses
the shared Finite identity, registers the agent's device state, and publishes
an agent profile (skippable with --skip-agent-profile):
finitechat hermes init --server https://chat.finite.computer \
--agent-name "My Agent"The agent home defaults to ~/.finite/agent; override it with
--agent-home DIR (or FINITE_AGENT_HOME). For local development, run a
local delivery server and point init at it instead:
cargo run -p finitechat-server -- serve 127.0.0.1:8787 --sqlite .state/finitechat.sqlite3
finitechat hermes --agent-home .state/agent init --server http://127.0.0.1:8787Then install the Finite Chat plugin into the Hermes agent (Hermes >= 0.16 plugin layout):
finitechat hermes installinstall writes the embedded finitechat plugin into
$HERMES_PLUGINS_DIR/finitechat, $HERMES_HOME/plugins/finitechat, or
~/.hermes/plugins/finitechat, plus a local finitechat.env recording the agent
home and binary path (defaults only; explicit Hermes config and process
environment win). Flags: --plugins-dir DIR or --plugin-dir DIR to place
it elsewhere, --plugin-name NAME, --finitechat-bin PATH,
--service-url URL to point the plugin at a supervisor-managed
finitechat hermes serve process, --force to overwrite, --json for
parseable output.
Enable the plugin in ~/.hermes/config.yaml:
plugins:
enabled:
- finitechat
gateway:
platforms:
finitechat:
enabled: trueThen hermes gateway start brings the agent onto Finite Chat.
When the Hermes gateway starts with the Finite plugin, it prints a QR code, a
finite://join?... URL, and a rotating 6-digit PIN. A human scans or pastes
that into the Finite Chat app and types the PIN; the agent verifies the PIN
proof before admitting them to the encrypted room. You can also drive invites
directly:
finitechat hermes invite --room-name "Ops" --json # create an invite (URL + PIN)
finitechat hermes invite-status --url INVITE_URL --json
finitechat hermes join --url INVITE_URL # join someone else's inviteFor the full agent integration surface (message polling, sending, the
supervised hermes serve bridge, smoke tests, and hardening evidence), see
integrations/hermes/README.md.
Everything below is for understanding, running, or modifying Finite Chat itself. Rust owns protocol state, persistence, networking, and product policy; SwiftUI renders the Rust-owned app state and dispatches typed actions.
The v1 product shape is a phone chat app for people and agents:
- Nostr keys provide portable account identity and profile discovery.
- OpenMLS protects room contents and membership truth.
- The HTTP server orders opaque encrypted payloads, persists delivery state, and never reads message contents.
- Offline text sends are durable, explicit retry is required after failure, and attachment upload stays online-only.
- Hermes integration uses the same chat surface as human conversations.
crates/finitechat-core- Rust app/runtime facade used by CLI and iOS.crates/finitechat-client- device state machine and encrypted local store.crates/finitechat-server- Axum HTTP delivery server with SQLite durability.crates/finitechat-proto/finitechat-http- wire DTOs and route contracts.crates/finitechat-mls- OpenMLS helpers and finite device credentials.crates/finitechat-cli- thefinitechatbinary: auth, Hermes bridge, and server calls.crates/finitechat-rmp- UniFFI, XCFramework, Xcode, and simulator helper.ios- SwiftUI app shell forcomputer.finite.finitechat.integrations/hermes/finitechat- Hermes platform plugin adapter.docs/adranddocs/protocol-v1.md- current product/protocol decisions.
The production/default app server is https://chat.finite.computer. Local
server URLs are explicit development and test overrides only.
For a friend self-building the native app on their own Mac and phone, start
with docs/friends-alpha-self-build.md. That runbook covers branch checkout,
generated iOS bindings/project files, Apple signing, clean physical-device
install, and confirming the app is using the deployed server instead of a local
development override.
For server iteration or local automated testing, start a local delivery server:
cargo run -p finitechat-server -- serve 127.0.0.1:8787 --sqlite .state/finitechat.sqlite3Run the iOS simulator app against that server with an explicit override:
FINITECHAT_SERVER_URL=http://127.0.0.1:8787 cargo run -p finitechat-rmp -- run iosTo test the iOS app surface with a real local Hermes gateway, use the bundled runner instead of the plain server command. This is a low-level local runner, not the physical-phone canary gate:
scripts/hermes-real-gateway-demo.shIn another terminal, point the simulator app at the runner's local server:
FINITECHAT_SERVER_URL=http://127.0.0.1:18788 cargo run -p finitechat-rmp -- run iosThe Hermes runner needs a prepared Hermes checkout with a .venv; set
FINITECHAT_HERMES_REPO=/path/to/hermes-agent if it is not in a sibling
checkout. It also needs the model provider key used by the Hermes profile. The
runner loads .env when present, or set
FINITECHAT_HERMES_ENV_FILE=/path/to/provider.env.
For the hardened "fresh Hermes instance to Paul's phone" quality loop, use
docs/hermes-phone-canary-loop.md. That runbook defines Finite Chat's local
phone and remote Docker gates and the evidence required before a human invite
is handed out.
The product-shaped hosted Hermes runtime ladder for local Docker, remote
Docker, and Phala belongs to
../finitecomputer-v2/docs/hermes-runtime-test-matrix.md. Finite Chat's local
loop proves the app/protocol/plugin contract; v2 proves the hosted-agent
runtime image and provider deploy shapes.
For team testing, the normal Hermes phone canary is:
cp .env.example .env
# Fill in one model provider key in .env, usually OPENROUTER_API_KEY.
xcrun devicectl list devices
scripts/hermes-phone-canary.py \
--install-phone-app \
--ios-device <device identifier or hardware UDID> \
--ios-development-team <Apple team id> \
--keep-runningThe script uses https://chat.finite.computer, builds the current
finitechat binary, installs the current iOS app on the paired phone, starts
real Hermes 0.17 with the finitechat plugin, proves invite admission
with a throwaway client, requires a real model reply, then prints the human
invite URL, report path, and stop.sh. Do not hand an invite to a
human from lower-level scripts that have not produced a passed report.
Remote Docker is the next promotion layer for teammates with access to the builder host:
scripts/hermes-remote-docker-canary.py --keep-runningThat wrapper requires a passed local phone report by default, builds the real
runtime image on ssh://finite-lat-2, proves real Hermes chat before and after
entrypoint backup/restore, and only then prints the invite URL for the restored
container.
The normal app flow is:
- Sign in with an
nsecor create a local Nostr identity. - Use People to open an existing profile or Scan to scan/paste an
invite URL or
npub. - Chat from the room surface. Rust owns send state, retry state, delivery projection, and attachment download decisions.
Fast Rust/server checks:
cargo fmt --all --check
cargo clippy --all-targets -- -D warnings
cargo test --workspace
cargo test -p finitechat-server --test http_routes
cargo test -p finitechat-server --test http_persistence
cargo test -p finitechat-server --test http_conformanceiOS checks:
cargo run -p finitechat-rmp -- doctor
cargo run -p finitechat-rmp -- bindings swift
cargo run -p finitechat-rmp -- test ios-simulatorfinitechat-rmp test ios-simulator owns the simulator test lifecycle: it
creates or reuses a dedicated RMP simulator, shuts it down, erases it, runs the
full FiniteChat test scheme with isolated derived data and .xcresult output
under .state, then terminates and shuts the simulator down. Use --json when
automation needs the resolved UDID and result bundle path.
Hermes/Python checks:
uvx --no-config ruff format --check .
uvx --no-config ruff check .
uvx --no-config --with hermes-agent basedpyright
python3 -m unittest discover -s tests -p '*test*.py'Pushing a v* tag runs .github/workflows/release.yml, which builds the
finitechat binary for linux-x86_64, macos-aarch64, and macos-x86_64 and
attaches finitechat-<platform>.tar.gz plus .sha256 checksums to the
GitHub release. The install block at the top of this README consumes those
assets.
The repo is intended to publish as finitecomputer/finitechat.
Tracked source excludes local and generated state:
.env, key files, SQLite stores, and.state/are ignored.target/, generated Xcode projects, Swift bindings, and XCFrameworks are ignored.- iOS signing uses
ios/project.yml; the generated.xcodeprojis local.
Before pushing, verify the GitHub target is the new repo. If
finitecomputer/finitechat resolves to finitecomputer/finitechat-old, do not
push or force-push; create or restore the new finitecomputer/finitechat repo
first.
This repo owns the Finite Chat server source, HTTP contract, and release gate
for https://chat.finite.computer. Hosted Finite Computer SaaS rollout
mechanics belong in ../finitecomputer-v2, which owns the current chat-server
deploy lane, stack deploy coordination, and hosted runtime matrix. The legacy
../finitecomputer repo remains for box1/TRF users while they are unmigrated.
Do not ship a native app/TestFlight build that depends on server behavior until
the deployed chat server has been verified against the finite-chat commit being
shipped.
The production health endpoint must identify the deployed server build:
cargo run -q -p finitechat-cli -- http --server https://chat.finite.computer healthExpected production output includes status: "ok", server_version,
server_contract_version, source_commit, and source_dirty: false. If
server_contract_version or source_commit is missing, the production server
is an old build and the app release is blocked until ../finitecomputer-v2
deploys a compatible finite-chat commit. See
docs/server-deployment-gate.md for the required handoff and verification
steps.
For iOS beta distribution, see docs/testflight-runbook.md. Finite Chat uses
bundle ID computer.finite.finitechat.