Self-hosting (alpha)
By default the Vulkro for Salesforce console runs on your own machine:
vulkro-sf serve boots a local web app, binds to loopback
(127.0.0.1), and opens your browser. Nothing leaves the box.
Self-hosting changes one thing: instead of every developer running the
console on their own laptop, you run one console on a shared box
and the team points their browsers at it. The scanner is still the same
point-in-time, offline engine. It does not phone home, and it still
reads a live org only through each operator's own sf CLI login.
The console ships no built-in authentication and no multi-tenant isolation in this release. Anyone who can reach the port gets the full console. Treat the steps below as a way to share the tool inside a trusted network, behind a reverse proxy that adds TLS and auth. Do not put it on the public internet as-is.
The model
- Point-in-time, offline scanner. A scan reflects the code and (if you run the org passes) the org configuration at the moment it ran. There is no always-on agent and no background polling.
- One shared console, many viewers. The self-hosted container holds the projects, scans, and triage decisions in a SQLite database on a mounted volume. Everyone on the box's network sees the same state.
- Org access stays per-operator. The live-org subcommands still
shell out to the
sfCLI. The container does not hold a Salesforce token. See Live-org setup for the precise privacy contract. - No vendor round-trip. The console does not send scans to Vulkro. Self-hosting does not change that; it only moves where the binary runs.
Environment variables
The same three variables drive both vulkro-sf serve directly and the
container.
| Variable | Default | Purpose |
|---|---|---|
VULKRO_SF_BIND | 127.0.0.1 | Bind IP. Loopback keeps the console local-only. Set to 0.0.0.0 to expose it on the network (then put a reverse proxy in front; see the caveat below). |
VULKRO_SF_PORT | OS-picked free port | Pin a fixed port. The --port flag overrides this variable. |
VULKRO_SF_DATA_DIR | ~/.vulkro-sf/data | Directory for the SQLite database and on-disk state. Point it at a mounted volume so data survives container restarts. |
When vulkro-sf serve binds to a non-loopback address it prints a
warning to that effect, because the console has no auth of its own:
WARNING: bound to a non-loopback address. Put this behind a reverse
proxy with authentication; the console has no built-in auth yet.
Docker quickstart
The build context is the repository root because the image compiles the whole cargo workspace and builds the web UI bundle.
With docker compose (recommended)
A ready-to-edit compose file lives at
deploy/self-hosted/docker-compose.yml. It defines one service, a
named volume for the data directory, and a commented-out reverse-proxy
stub.
cd deploy/self-hosted
docker compose up -d --build
By default the compose file maps the console to 127.0.0.1:8923 on the
host, so it is reachable only from the box itself. Reach it over an SSH
tunnel from your laptop:
ssh -L 8923:127.0.0.1:8923 user@your-host
# then open http://127.0.0.1:8923 in your browser
With plain docker
# Build (run from the repo root):
docker build -f crates/vulkro-sf/Dockerfile -t vulkro-sf:local .
# Run, persisting data to a named volume, console on loopback:
docker run -d \
--name vulkro-sf \
-p 127.0.0.1:8923:8923 \
-v vulkro_sf_data:/data \
vulkro-sf:local
The image sets VULKRO_SF_BIND=0.0.0.0, VULKRO_SF_PORT=8923, and
VULKRO_SF_DATA_DIR=/data so it binds predictably inside the container
and stores state on the volume. It runs vulkro-sf serve --no-browser
as a non-root user.
Building the UI bundle
The container build runs npm run build:sf in an earlier stage and
copies the result into the cargo build, so a clean checkout builds
end-to-end with no extra steps. If you would rather build the UI on the
host, populate web-ui-sf/dist/ first and that stage becomes a fast
cache hit.
Put a reverse proxy in front
Because the console has no authentication, exposing it beyond a trusted network means terminating TLS and adding auth at a layer in front. The compose file ships a commented stub for both Caddy and nginx showing exactly where the TLS certs and the basic-auth credentials go.
The recommended shape:
- Keep the app container's published port on loopback (or move it to an internal-only Docker network).
- Run a reverse proxy (Caddy or nginx) that listens on
443, terminates TLS, enforces basic-auth or your SSO, and proxies to the app container. - Publish only the proxy.
This is an interim arrangement. Built-in authentication for the self-hosted console is on the roadmap; until then the proxy is the front door.
What this is not (yet)
- Not multi-tenant. All viewers share one project list and one triage queue. There is no per-user or per-team separation.
- Not authenticated at the app layer. Auth lives in the reverse proxy you place in front, not in the console.
- Not a managed service. You run and patch the box. Rebuild the
image to pick up a new
vulkro-sfrelease.
For the single-developer workflow, you do not need any of this: just run
vulkro-sf serve and use the console locally. See
Install and Your first scan.