Guide

REST and gRPC API

Ze exposes a programmatic API over REST (HTTP/JSON) and gRPC. Both transports share one engine -- they produce identical command output and support the same commands.

Quick Start

Enable REST in your config:

environment {
    api-server {
        rest {
            enabled true;
            server { ip 127.0.0.1; port 8081; }
        }
    }
}

REST uses plaintext HTTP and only binds loopback addresses. Use authenticated gRPC with TLS for management from a non-loopback address.

Or via environment variable:

ze.api-server.rest.enabled=true
ze.api-server.rest.listen=127.0.0.1:8081

Query the API:

curl http://localhost:8081/api/v1/commands
curl -X POST http://localhost:8081/api/v1/execute \
    -H "Content-Type: application/json" \
    -d '{"command":"show bgp summary"}'

Open interactive docs: http://localhost:8081/api/v1/docs

Authentication

Ze selects one of three API authentication modes:

Mode User or token source Client credentials
Per-user A zefs user or a system.authentication.user entry Authorization: Bearer username:password
Single token ze.api-server.token or YANG api-server { token "secret"; } Authorization: Bearer <secret>

Per-user mode has precedence. If at least one user exists, the shared token does not act as a fallback for a failed per-user login.

The per-user source merges the zefs users with system.authentication.user entries from the running config. A config user replaces a zefs user with the same name. Only zefs users that survive this merge keep the zefs recovery profile. A successful login carries its authorization view with the authenticated request. Concurrent requests with the same username cannot replace each other's resolved profiles.

At boot, Ze reads the merged user source once after it populates the config provider. Ze refuses startup before management listeners bind if that read fails. An environment.ssh block is not required for API users or no-BGP AAA.

Each request uses the current accepted authentication generation. A successful reload publishes one generation that adds, changes, or removes API users and authorization policy atomically. The generation includes profile definitions and user-profile assignments. A failed reload keeps the prior credentials and policy. The zefs user list stays the boot snapshot.

Per-user requests keep the authenticated username and the authorizer from that authentication result. Single-token and no-auth requests use a reserved server-injected identity. A valid shared token has write authority. A no-auth request stays read-only, so Ze denies writes before command authorization.

Startup output and remedies

Ze writes one of these exact lines when an API server is configured:

API auth mode: per-user (1 users)
API auth mode: single-token (shared bearer)
warning: API auth mode: NONE (no users, no token) -- set ze.api-server.token or initialize zefs

The per-user count includes surviving zefs users and config users. To leave single-token or NONE mode, add a config user or initialize zefs. To keep shared credentials, set ze.api-server.token.

REST Endpoints

Command execution

Method Path Description
GET /api/v1/commands List all commands with metadata
GET /api/v1/commands/{path} Describe one command (e.g., /bgp/summary)
POST /api/v1/execute Execute any command
GET /api/v1/execute/stream?command=... Stream a registered streaming command, for example monitor event, as Server-Sent Events. It uses the same handler registry, authorization, and accounting path as SSH monitor commands.
GET /api/v1/complete?partial=... Tab completion (future)

A plugin command registered with Hidden true is absent from /api/v1/commands, as it is from completion, from help, and from the MCP tool list. buildCommandMeta is the one source those surfaces read, so the flag has one meaning everywhere.

POST /api/v1/execute body:

{
  "command": "show bgp rib",
  "params": {"family": "ipv4/unicast"}
}

Response:

{
  "status": "done",
  "data": { ... }
}

Convenience routes

Most convenience routes map to the generic Execute endpoint internally. The refresh row gives the required generic Execute command instead.

Method Path Maps to
GET /api/v1/peers show bgp summary
GET /api/v1/peers/{name} show bgp peer {name} detail
DELETE /api/v1/peers/{name} request peer {name} teardown
POST /api/v1/peers/{name}/refresh Use /api/v1/execute with request peer {name} refresh {family}
GET /api/v1/rib/{family} show bgp rib family {family}
GET /api/v1/rib/{family}/best show bgp rib best family {family}
GET /api/v1/system/version show version
GET /api/v1/system/status show status
POST /api/v1/system/reload request reload

Config editing

Method Path Description
GET /api/v1/config/running Current running config
POST /api/v1/config/sessions Start a candidate session, returns {"session-id": "..."}
PUT /api/v1/config/sessions/{id} Set a value: {"path":"bgp.router-id","value":"10.0.0.1"}
DELETE /api/v1/config/sessions/{id}/{path} Delete a config path
GET /api/v1/config/sessions/{id}/diff Preview pending changes
POST /api/v1/config/sessions/{id}/commit Apply changes
DELETE /api/v1/config/sessions/{id} Discard session

Sessions are owned by the authenticated user. Another user cannot access a session they did not create (returns 403 Forbidden). Idle sessions expire after 30 minutes.

No-auth REST/gRPC callers cannot create config sessions. Configure a token or per-user authentication for API-driven config changes.

Documentation

Path Description
/api/v1/openapi.json OpenAPI 3.1 specification (auto-generated from YANG)
/api/v1/docs Interactive Swagger UI (assets vendored, offline-capable)

The OpenAPI spec is generated lazily on first request so it captures all plugin commands registered during startup. Documentation routes use the same Bearer authentication policy as the API when auth is configured.

gRPC Services

Proto definitions: api/proto/ze.proto, package ze.api.v1.

Enable gRPC:

environment {
    api-server {
        grpc {
            enabled true;
            server { ip 127.0.0.1; port 50051; }
        }
    }
}

Loopback gRPC can run plaintext for local tooling. Non-loopback gRPC listeners must be authenticated and must configure TLS with both tls-cert and tls-key.

ZeService

Generic command execution and discovery.

RPC Type Purpose
Execute unary Run a command, get result
Stream server-stream Stream a registered streaming command, for example monitor event, over a gRPC server stream. It uses the same handler registry, authorization, and accounting path as SSH monitor commands.
ListCommands unary Enumerate all commands
DescribeCommand unary Metadata for one command
Complete unary Tab completion (future)

CommandResponse.data is JSON-encoded bytes for identical content with REST.

ZeConfigService

Typed config session management (same semantics as REST config sessions).

RPC Purpose
GetRunningConfig Current running config
EnterSession Start a candidate session
SetConfig / DeleteConfig Modify the candidate
DiffSession Preview pending changes
CommitSession Apply changes
DiscardSession Throw away changes

gRPC authentication

Pass the same Bearer username:password or Bearer <token> as REST, via the authorization metadata key:

metadata = [('authorization', 'Bearer alice:password123')]
stub.Execute(CommandRequest(command='show bgp summary'), metadata=metadata)

gRPC reflection

Reflection is enabled by default. Discover the schema with grpcurl on a plaintext loopback listener:

grpcurl -plaintext localhost:50051 list
grpcurl -plaintext localhost:50051 describe ze.api.v1.ZeService
grpcurl -plaintext -d '{"command":"show bgp summary"}' \
    -H "authorization: Bearer alice:password123" \
    localhost:50051 ze.api.v1.ZeService/Execute

TLS

Configure TLS via YANG before binding gRPC outside loopback:

environment {
    api-server {
        grpc {
            enabled true;
            tls-cert "/etc/ze/server.pem";
            tls-key "/etc/ze/server.key";
        }
    }
}

Both fields must be set together. Minimum TLS version is 1.2. Startup fails if an authenticated non-loopback gRPC listener is configured without TLS.

CORS

For browser-based clients, set an allowed origin:

environment {
    api-server {
        rest {
            enabled true;
            cors-origin "https://dashboard.example.com";
        }
    }
}

Preflight OPTIONS requests are handled automatically.

Environment Variables

Variable Default Description
ze.api-server.rest.enabled false Enable REST API server
ze.api-server.rest.listen 0.0.0.0:8081 REST listen address schema default; only loopback is accepted at server startup, so set this to ::1:<port> when enabling REST. 127.0.0.1:8081
ze.api-server.grpc.enabled false Enable gRPC API server
ze.api-server.grpc.listen 0.0.0.0:50051 gRPC listen address; non-loopback requires authentication and TLS
ze.api-server.token (empty) Single bearer token (if per-user auth not wanted)

Precedence: env > YANG config. Values set in env override YANG.

Input Validation

Both transports validate command input against shell injection:

These checks prevent command tokenizer confusion when user input flows into dispatcher command strings.

Differences Between Transports

The transports are functionally equivalent. Pick based on client needs:

Feature REST gRPC
Discovery OpenAPI 3.1 + Swagger UI gRPC reflection + grpcurl
Streaming Internal SSE hook, production hub returns streaming not supported Internal server-stream RPC, production hub returns streaming not supported
Browser support Needs grpc-web proxy
Tooling curl, any HTTP client grpcurl, any gRPC client
Overhead JSON Protobuf (smaller wire format)
TLS via reverse proxy today built-in