Your own mesh
The quick start joined a mesh that someone else runs. This page runs one for
you: a control plane, a router and a web console on your laptop, in one
process, with two nodes talking through it. One node publishes a model that
runs on your machine, the other calls it. sam-one runs the same code as a
Kubernetes deployment, so what you learn here applies there too.
You need the sam-one and sam-node binaries. The
install script provides both. The model is served
by Ollama; any OpenAI-compatible server works in its
place.
1. Start the control plane
sam-one --data-dir ~/sam-one
--data-dir holds the database, the router’s key and the generated tokens.
If you delete it, you get a new mesh. After a moment sam-one prints a
banner:
══════════════════════════════════════════════════════════════════
SAM standalone mesh is ready!
API URL: http://0.0.0.0:33775
Web Console: http://0.0.0.0:33775/console
Router Peer: 12D3KooWBzUDQCkZhz2rWrYBhpjcCH8VnrRNcwCW6DoF36iADYrY
Admin Token: sam_adm_…
Join Token: sam_tok_…
To enroll a node:
sam-node join http://0.0.0.0:33775 --bootstrap-token-path /home/you/sam-one/join-token
══════════════════════════════════════════════════════════════════
sam-one picked a free port. Pass --port 8080 for a fixed one. The join
token is a standing bootstrap token that lets nodes enroll without an
identity provider. The admin token authenticates the console and the
admin API. Both are also written to files in the data directory, and the rest
of this page reads them from there.
On first boot, sam-one seeds an open development policy and logs a warning
about it: any enrolled node may register any service and call any service.
This is a reasonable default for a laptop and a bad one for anything shared.
The last section explains how to replace it.
2. Publish a model from one node
A node publishes backends that speak one of three protocols, and the
service type says which: inference for an OpenAI-compatible API, mcp
for an MCP server, a2a for an A2A agent. The type is a contract, not a
label. The node speaks that protocol to the backend and to nobody else: an
inference backend is offered for the models it lists on /v1/models, an
mcp server is advertised once the node has completed an MCP session with
it, an a2a agent once it has served its agent card. A plain web server
declared under any of these types is never advertised. The mesh is not a
generic HTTP tunnel.
Pull a small model and declare Ollama as an inference service. The
target_url is the backend’s root, without /v1; the node adds the prefix:
ollama pull gemma3:1b
cat > ~/node-a.yaml <<'EOF'
version: "v1alpha1"
services:
- type: inference
name: laptop-llm
description: "Ollama on my laptop"
target_url: "http://127.0.0.1:11434"
EOF
Then run a node with it. Substitute the port from your banner:
URL=http://127.0.0.1:33775
sam-node run --control-plane $URL \
--bootstrap-token-path ~/sam-one/join-token \
--config ~/node-a.yaml \
--data-dir ~/node-a --bind-addr= --allow-loopback --listen /ip4/127.0.0.1/tcp/0
Three of these flags are only needed because both nodes run on one machine.
--bind-addr= (an empty value) keeps the node’s local API on its Unix
socket, so the two nodes do not compete for port 8080. --allow-loopback
lets the nodes advertise and dial 127.0.0.1. --listen .../tcp/0 picks a
free peer-to-peer port. On separate machines you would not pass any of them.
The node enrolls with the join token, connects to the router and prints its peer ID:
SAM Node Online.
PeerID: 12D3KooWSCnbUoZ8Jv3EKGv17LqEWtnTMfZ3XYJUg2WTm5Gz2hUK
Ollama is never exposed on the network. It listens on loopback, and the only way to it from another machine is through this node, which checks the caller’s credential and the mesh policy on every request.
3. Call it from another node
In a second terminal, start a node with no services:
URL=http://127.0.0.1:33775
sam-node run --control-plane $URL \
--bootstrap-token-path ~/sam-one/join-token \
--data-dir ~/node-b --bind-addr= --allow-loopback --listen /ip4/127.0.0.1/tcp/0
Every node’s local API includes an OpenAI-compatible endpoint. /v1/models
lists the models that every reachable inference provider serves, and a
completion request is routed to a provider of the model it names. Ask node
B:
SOCK=~/node-b/sam.sock
curl -s --unix-socket $SOCK http://localhost/v1/models
curl -s --unix-socket $SOCK http://localhost/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{"model":"gemma3:1b","messages":[{"role":"user","content":"Say hello in five words."}]}'
The model list shows gemma3:1b with node A’s peer ID as owned_by. Allow
a few seconds after A starts for B to learn about it. Nodes announce their
services in a discovery table that the router hosts, and the announcement
takes a moment to arrive.
The request went from B’s socket to B, then over an authenticated connection
to A, through A’s policy check, to Ollama, and back. Both nodes verified the
other’s credential before any data moved. If A and B cannot reach each other
directly, the connection is relayed through the router inside sam-one,
which carries ciphertext and learns only that the two are talking.
The socket needs no token: only your user can open it. To point an OpenAI
SDK at the mesh instead, give node B a TCP port with
--bind-addr 127.0.0.1:8081 and a token as in the
quick start, then use
http://127.0.0.1:8081/v1 as base_url and the token as api_key.
4. Look at it in the console
Open http://127.0.0.1:33775/console and paste the admin token. The console
shows the enrolled nodes, the router, the services each node reports, the
bootstrap tokens with their remaining uses, and the mesh policy. You can edit
the policy in place.
The same operations are available from the command line. sam-one is also
an admin client for a running server. It reads the admin token from the data
directory, from SAM_ADMIN_TOKEN, or from --admin-token-path, never from a
flag value:
sam-one token list --server $URL --data-dir ~/sam-one
sam-one token create --server $URL --data-dir ~/sam-one --description "node c" --max-usages 1
sam-one token revoke <token-id> --server $URL --data-dir ~/sam-one
sam-one admin ban <peer-id> --server $URL --data-dir ~/sam-one
MCP servers and A2A agents
The other two service types are declared the same way and reached through
the same node. An MCP server gives agents tools; the quick start
calls one on the testnet through
the node’s MCP endpoint. A stdio server is declared with command and the
node runs it; a Streamable HTTP server with target_url:
- type: mcp
name: filesystem
command: ["npx", "-y", "@modelcontextprotocol/server-filesystem", "/srv/docs"]
An A2A agent is another agent, spoken to with the A2A protocol. It is
reached at /sam/<peer-id>/a2a/<name>/ on the caller’s node, which
regenerates the agent card so that a stock A2A client works unchanged:
- type: a2a
name: triage
target_url: "http://127.0.0.1:9999"
Exposing services covers all three types and what the policy must grant. The A2A chat and Gemini Buddy use cases run a complete agent behind each of the other two types.
Reaching it from other machines
Everything above used http://127.0.0.1. sam-node accepts a plain http
URL only when the control plane is on the same host. A node on another
machine needs an https URL, because the control plane is the node’s trust
root and SAM refuses to fetch it over plaintext from a remote address.
On a laptop behind NAT, the quickest way to get an https URL is a temporary
tunnel:
sam-one --data-dir ~/sam-one --tunnel cloudflare
This publishes the port on a random trycloudflare.com hostname, with no
account needed, and prints that URL in the banner. If cloudflared is not
installed, sam-one offers to download a pinned, checksum-verified release
into the data directory. --tunnel-install accepts the download without
asking. Nodes on any network then enroll with the same
sam-node run --control-plane <url> --bootstrap-token-path <file> command,
without the loopback flags.
If you have a real hostname and a reverse proxy in front, pass
--external-url https://mesh.example.com instead. The
Cloud Run guide shows a hosted variant.
With --tunnel or any other https URL, sam-one also prints a QR code
that enrolls a phone running the SAM Connect app. That app is in
preview.
Before you rely on it
- Policy: replace the open development policy. Write a
mesh policy file and start
sam-onewith--policy-fileon a fresh data directory, or edit the policy in the console. Once the database has a policy, the database is the source of truth. - Enrollment: run with
--no-join-tokenso nodes can only enroll with tokens you mint (sam-one token create, single-use by default), or give the mesh an identity provider with--issuerand let people log in. - State: keep the data directory on durable storage, or point
--db-driver postgres --db-dsn ...at a database. - Tokens in logs: read the admin token from
SAM_ADMIN_TOKENor--admin-token-pathinstead of the banner, and pass--enroll-qr=falsein non-interactive environments.
The sam-one reference lists every flag.