Run PULSE locally
There are two ways to run PULSE on your own machine. The CORE loop runs the whole stack: backend, inference, and the Flutter web app. The direct binary runs only the backend, which is enough to drive and test every non-AI RPC.
Prerequisites
- Go 1.26 (see
grpc/go.mod). - Flutter, for the client.
- The sibling repositories checked out next to
pulse/:core,inference,security,mesh,store,ui, andbilling. The backend does not build without them, becausegrpc/go.modpoints at them withreplacedirectives. grpcurl, to call the API by hand.
Option 1: the CORE loop
From the core repository:
cd ../core/grpc # CORE's Go module; set PULSE_REPO to an absolute path for `dev up`
PULSE_REPO=/abs/path/to/pulse go run ./cmd/core dev up pulseThis builds the backend and starts it on a free port. It starts the shared
inference server when the model weights are present, then builds and serves the
Flutter web app pointed at the backend’s real port. CORE provisions the
inference binaries and weights under ../core/artifacts/.
Option 2: the backend binary
Build
Generated protobuf code is committed under grpc/api/, so you do not need to
run buf to build.
cd grpc && go build -o bin/pulse-server . && cd ..Start the server
cd grpc
PULSE_ADMIN_PASSWORD='<choose-a-password>' \
PULSE_JWT_SECRET="$(openssl rand -hex 32)" \
PULSE_GRPC_REFLECTION=true \
./bin/pulse-server serve --port 52000 > /tmp/pulse.log 2>&1 &Three variables matter here:
PULSE_JWT_SECRETis required. Without itserveexits withrefusing to start: no session signing key configured. Any value you generate works locally. It must never be a value that has been committed to a repository.PULSE_ADMIN_PASSWORDseeds a break-glass admin with usernameadmin. Without it the log saysskipping admin seed; break-glass login unavailable, and every login fails.PULSE_GRPC_REFLECTION=trueturns on gRPC server reflection sogrpcurlcan list services. It is off by default. Never set it on a deployed instance.
With no --port (and no PORT variable), the server picks a free port.
pkill -f 'pulse-server serve'. That pattern also
matches the shell running the command, so it kills your own command and looks
like a server crash. Kill the process by its PID instead.Wait for health
until curl -s localhost:52000/health | grep -q '"status":"ok"'; do sleep 2; done
curl -s localhost:52000/healthThe response looks like {"status":"ok","model_ready":false,"port":52000}.
model_ready stays false until an inference server has loaded its weights.
The HTTP status is always 200. PULSE serves every non-AI feature without a
model.
Sign in and call an RPC
TOK=$(grpcurl -plaintext -d '{"username":"admin","password":"<your-password>"}' \
localhost:52000 pulse.v1.IdentityService/Login | jq -r '.accessToken')
grpcurl -plaintext -H "authorization: Bearer $TOK" -d '{}' \
localhost:52000 pulse.v1.CampaignService/ListCampaignsThe token is in the accessToken field. The repository also ships a smoke
driver that runs health, login, whoami, and list calls in one go:
PULSE_PASSWORD='<your-password>' go run .claude/skills/run-pulse/smoke.go 52000Without a model
A bare machine has no mistralrs-server and no model weights. The server still
starts and serves login, every read, and every create or update. AI calls fail
with inference server is not running. That is the expected result, not a bug.
Run the Flutter client
cd flutter
flutter run -d chrome # web
flutter run -d linux # desktop
flutter run # a connected Android deviceThe client reads its backend address from PULSE_BACKEND_URL, a
--dart-define that defaults to http://localhost:50051. For the web build it
is fixed at compile time, which is why core dev up rebuilds the bundle when
the backend port changes.
Run the tests
cd grpc && go test ./...
cd flutter && flutter analyze