/docs / app-sidecar

App Sidecar

A sidecar is a binary that runs next to an app. It serves the page's API, keeps the app's data, and gives the app's employee tools. Nebo starts it, routes requests to it over a local socket, and restarts it when it fails.

The contract

A sidecar is a gRPC server on a Unix socket. Listen on the path in NEBO_APP_SOCK and implement apps.v0.UIService. Nebo calls HandleRequest for every request; HealthCheck and Configure are part of the service but are not called today. The socket must appear within the manifest's startup_timeout, which defaults to 10 seconds and may be at most 120.

syntax = "proto3";
package apps.v0;

service UIService {
  rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse);
  rpc Configure(SettingsMap) returns (Empty);
  rpc HandleRequest(HttpRequest) returns (HttpResponse);
}

message HttpRequest {
  string method = 1;               // GET, POST, PUT, DELETE, ...
  string path = 2;                 // relative to /apps/{id}/api/
  string query = 3;                // raw query string
  map<string, string> headers = 4;
  bytes body = 5;
}

message HttpResponse {
  int32 status_code = 1;
  map<string, string> headers = 2;
  bytes body = 3;
}

Sidecars run on macOS and Linux.

Where Nebo finds the binary

In the app's folder, Nebo uses the first of these that exists:

  1. A file named binary
  2. A file named app
  3. The first file in tmp/
  4. The first file in bin/
  5. The first executable in sidecar/target/release/, for local development

The binary must be a regular, executable file, not a symbolic link, and at most 500 MB. Published apps carry it at bin/<name>. When the file changes on disk, Nebo restarts the sidecar.

Environment

The sidecar starts with an empty environment and these variables:

VariableValue
NEBO_APP_ID, NEBO_APP_NAME, NEBO_APP_VERSIONThe app’s identity.
NEBO_APP_DIRThe app’s folder.
NEBO_APP_SOCKThe socket to listen on, <app folder>/<id>.sock. Nebo sets its mode to 0600.
NEBO_DATA_DIRThe app’s data folder, <data>/appdata/agents/<id>/. Also the working directory.
NEBO_API_URLThe bot’s local API, http://127.0.0.1:<port>.
NEBO_APP_TOKENA token for calling the bot’s app API.
PATH, HOME, TMPDIR, LANG, LC_ALL, TZPassed through.

Standard output and standard error go to sidecar.log in the data folder. Standard input is a pipe; end of file means Nebo has gone away and the sidecar should exit. The sidecar runs in its own process group.

Requests from the page

A request to /apps/<id>/api/<path>, which the page makes with neboFetch and a relative path, reaches HandleRequest with path set to <path>. Paths that start with _ are refused. Request bodies are limited to 10 MB and responses to 32 MB.

Tools for the employee

Declare tools in the app's agent.json. Each becomes a tool named app__<app>__<name>, loaded when the employee needs it. A call becomes a request to the sidecar at method and path.

"tools": [
  {
    "name": "list_deals",
    "description": "List open deals, optionally filtered by stage.",
    "method": "GET",
    "path": "/deals",
    "input_schema": { "type": "object", "properties": { "stage": { "type": "string" } } }
  }
]

Calling Nebo

The sidecar can use the same services as the page by calling $NEBO_API_URL/api/v1/apps/$NEBO_APP_ID/... with the header Authorization: Bearer $NEBO_APP_TOKEN.

RouteDoes
GET /storage, GET|PUT|DELETE /storage/<key>The app’s key-value store.
POST /agents/invoke, POST /agents/streamAsks an employee: {message, agent, data}. Stream responses are server-sent events.
POST /janus/complete, POST /janus/streamA direct model call: {messages, model, temperature, max_tokens, system}.
POST /http/proxyAn outside request, {url, method, headers, body}, allowed by the app’s network: permissions.
GET /identityThe app’s employee.

Lifecycle

  • Nebo probes the socket every three seconds.
  • A failed sidecar restarts after one second, doubling to at most 60 seconds. After five failures in a row it is marked failed. A sidecar that stays up for 60 seconds is healthy again.
  • A request to a stopped sidecar starts it and waits up to 15 seconds. GET and HEAD requests are retried.
  • To stop a sidecar, Nebo sends SIGTERM to its process group, then kills it after two seconds.