← Back to Scope

THE FIRST SIGNAL

INITIALIZE.
GET CONNECTED.

Deploy Scope from prebuilt container images, or run a local development stack from source. Then connect each machine with the one-command agent installer. The image deployment and monitored machines need no source checkout. The current landing page deploys from its npm workspace on Railway; the Compose example retains the last published landing image.

01 / SCOPE SERVERDocker Compose

Pulls the dashboard, API, and landing images with PostgreSQL and Redis.

02 / MONITORED MACHINELinux or macOS

One command from the dashboard. No Git, Go, or Docker required.

DEPLOY FROM IMAGES.

Scope publishes scope-backend and scope-web on Docker Hub under tiagomiguel29 for Linux x64 and ARM64. The example also retains the last published scope-landingimage. It adds PostgreSQL, Redis, and a one-time database migration. You choose how to host each component and which reverse proxy handles HTTPS, WebSockets, and gRPC.

01

DOWNLOAD THE DEPLOYMENT FILES.

These are the complete Compose file , and environment template. No repository clone is needed.

mkdir scope && cd scope curl -fsSLO https://raw.githubusercontent.com/tiagomiguel29/project-orion/main/deploy/compose.yml curl -fsSLo .env.example https://raw.githubusercontent.com/tiagomiguel29/project-orion/main/deploy/.env.example
02

SET ENDPOINTS AND SECRETS.

Copy the template to .env. Set the dashboard’s public HTTPS origin, the API URL, and the agent’s public gRPC address. Add a database password and three independent random secrets; openssl rand -hex 32 can generate each one. Keep this file private.

cp .env.example .env

The default main image tag tracks new builds. Pin SCOPE_IMAGE_TAG to a released version or digest for controlled production upgrades.

03

START THE STACK.

Compose pulls the images, waits for PostgreSQL and Redis, runs migrations once, then starts the backend, dashboard, and landing page on local ports. Connect the reverse proxy of your choice and open your dashboard hostname to create an account.

docker compose --env-file .env -f compose.yml up -d

Check progress with docker compose --env-file .env -f compose.yml ps. By default, the ports bind to 127.0.0.1: dashboard 3000, API/WebSockets 4000, gRPC 50052, and landing page 8080.

Your proxy should terminate TLS, forward WebSocket upgrades to port 4000, and speak HTTP/2 to the gRPC upstream on port 50052. The API and gRPC can share a public hostname when your proxy routes them separately. For backend replicas, replace the sample’s fixed host port bindings with your proxy’s service discovery or load balancer; Redis shares realtime state across replicas. A proxy container can also join the Compose network and route to the service names directly.

Once the landing page has a public HTTPS address, set SCOPE_LANDING_URL in .env for canonical links, social previews, and its sitemap. To enable Umami analytics, add the website ID as SCOPE_UMAMI_WEBSITE_ID. Umami Cloud is the default; a self-hosted instance can use SCOPE_UMAMI_SCRIPT_URL. All three settings are optional.

DEPLOYMENT TEMPLATE / YAML

THE COMPLETE COMPOSE FILE.

Shown in full below. Copy it or download the exact file from the repository.

# Pulls prebuilt Scope images; no source checkout or local image build required.
# Copy .env.example to .env, fill every required value, then:
#   docker compose --env-file .env -f compose.yml up -d
name: scope

services:
  postgres:
    image: postgres:17-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: scope
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}
      POSTGRES_DB: scope
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U scope -d scope"]
      interval: 5s
      timeout: 3s
      retries: 15

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 15

  migrate:
    image: tiagomiguel29/scope-backend:${SCOPE_IMAGE_TAG:-main}
    restart: "no"
    command: ["npm", "run", "migration:run:prod"]
    environment:
      DATABASE_URL: postgresql://scope:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}@postgres:5432/scope
    depends_on:
      postgres:
        condition: service_healthy

  backend:
    image: tiagomiguel29/scope-backend:${SCOPE_IMAGE_TAG:-main}
    restart: unless-stopped
    ports:
      - "${SCOPE_BIND_ADDRESS:-127.0.0.1}:${SCOPE_API_PORT:-4000}:4000"
      - "${SCOPE_BIND_ADDRESS:-127.0.0.1}:${SCOPE_GRPC_PORT:-50052}:50052"
    environment:
      HTTP_PORT: 4000
      GRPC_PORT: 50052
      DATABASE_URL: postgresql://scope:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}@postgres:5432/scope
      REDIS_URL: redis://redis:6379
      REQUIRE_REDIS: "true"
      DB_SYNCHRONIZE: "false"
      CORS_ORIGIN: ${SCOPE_WEB_ORIGIN:?Set SCOPE_WEB_ORIGIN in .env}
      WEBAUTHN_ORIGIN: ${SCOPE_WEB_ORIGIN:?Set SCOPE_WEB_ORIGIN in .env}
      AGENT_JWT_SECRET: ${AGENT_JWT_SECRET:?Set AGENT_JWT_SECRET in .env}
      USER_JWT_SECRET: ${USER_JWT_SECRET:?Set USER_JWT_SECRET in .env}
      MFA_ENCRYPTION_SECRET: ${MFA_ENCRYPTION_SECRET:?Set MFA_ENCRYPTION_SECRET in .env}
      SCOPE_SMTP_HOST: ${SCOPE_SMTP_HOST:-}
      SCOPE_SMTP_PORT: ${SCOPE_SMTP_PORT:-587}
      SCOPE_SMTP_USER: ${SCOPE_SMTP_USER:-}
      SCOPE_SMTP_PASSWORD: ${SCOPE_SMTP_PASSWORD:-}
      SCOPE_SMTP_FROM: ${SCOPE_SMTP_FROM:-}
      SCOPE_SMTP_SECURE: ${SCOPE_SMTP_SECURE:-false}
      SCOPE_SMTP_REQUIRE_TLS: ${SCOPE_SMTP_REQUIRE_TLS:-true}
    depends_on:
      migrate:
        condition: service_completed_successfully
      redis:
        condition: service_healthy

  web:
    image: tiagomiguel29/scope-web:${SCOPE_IMAGE_TAG:-main}
    restart: unless-stopped
    ports:
      - "${SCOPE_BIND_ADDRESS:-127.0.0.1}:${SCOPE_WEB_PORT:-3000}:3000"
    environment:
      SCOPE_PUBLIC_API_URL: ${SCOPE_API_URL:?Set SCOPE_API_URL in .env}
      SCOPE_PUBLIC_AGENT_SERVER_ADDRESS: ${SCOPE_AGENT_GRPC_ADDRESS:?Set SCOPE_AGENT_GRPC_ADDRESS in .env}
      SCOPE_PUBLIC_AGENT_TLS_DISABLE: ${SCOPE_AGENT_TLS_DISABLE:-false}
    depends_on:
      - backend

  landing:
    image: tiagomiguel29/scope-landing:${SCOPE_IMAGE_TAG:-main}
    restart: unless-stopped
    environment:
      SCOPE_LANDING_URL: ${SCOPE_LANDING_URL:-}
      SCOPE_UMAMI_WEBSITE_ID: ${SCOPE_UMAMI_WEBSITE_ID:-}
      SCOPE_UMAMI_SCRIPT_URL: ${SCOPE_UMAMI_SCRIPT_URL:-}
    ports:
      - "${SCOPE_BIND_ADDRESS:-127.0.0.1}:${SCOPE_LANDING_PORT:-8080}:80"

volumes:
  postgres_data:

START THE SCOPE SERVER.

Prefer source checkout for local development? These steps run the development stack. You’ll need Git and Docker Compose on this machine.

01

CLONE THE REPOSITORY.

Clone the open-source repository and enter the project directory.

git clone https://github.com/tiagomiguel29/project-orion.git scope cd scope
02

START THE STACK.

Start the dashboard, backend, PostgreSQL, and Redis. The initial build can take a few minutes.

docker compose -f docker-compose.dev.yml up --build

This configuration is for local development. For public deployments, configure your secrets, TLS, and allowed origins.

03

OPEN THE DASHBOARD.

Open localhost:3000 , create your account, and sign in. The dashboard is where you register each device and get its installation command.

INSTALL AN AGENT.

On the dashboard, select Add device. For an existing device, open its page and select Install agent. Each command is tied to one device and one short-lived pairing code.

04

SET REACHABLE ADDRESSES.

Enter the backend URL for pairing and the gRPC host and port for telemetry. Both must be reachable from the machine you’re installing on. Use an HTTPS URL and TLS-enabled gRPC endpoint for a remote machine. The default localhost:4000 API and localhost:50052 gRPC addresses work only when the agent and backend run on the same machine.

The dashboard’s plaintext gRPC option is for localhost development only. Public deployments need a trusted certificate and a reachable gRPC endpoint.

05

COPY AND RUN THE COMMAND.

Copy the complete command generated by Scope and run it in a terminal on the machine to monitor. You’ll need sudo access and curl. The installer selects the matching Linux or macOS x64/ARM64 binary from the latest release , checks its SHA-256 hash, pairs the device, and starts the service. The device ID and pairing code are unique; the example below shows the command’s shape, not values to paste.

curl -fsSL https://github.com/tiagomiguel29/project-orion/releases/latest/download/install.sh | sudo sh -s -- --server https://scope.example.com --grpc scope.example.com:443 --device DEVICE_ID --pair PAIRING_CODE

The pairing code expires after 10 minutes and works once. Get a fresh command through Install agent if it expires. Don’t share a command containing a live pairing code.

06

CHECK THE FIRST SIGNAL.

The installer reports that the agent started. Return to the device page and wait for its live metrics. On Linux, inspect the service with systemctl status scope-agent; on macOS, use launchctl print system/dev.scope.agent. A fresh dashboard command can reinstall or update the agent without deleting the device’s history.

The binary lives at /usr/local/bin/scope-agent, its private configuration at /etc/scope/.env, and buffered telemetry at /var/lib/scope/wal.db.

CONFIGURE NOTIFICATIONS.

Notifications start disabled. Sign in as an admin and open the bell in the dashboard header. The page shows recent alerts and delivery attempts alongside the settings. Thresholds are evaluated when new telemetry arrives; offline alerts fire after the configured timeout.

07

CHOOSE GLOBAL RULES.

Enable notifications, then set the offline timeout in seconds, CPU/RAM/disk thresholds in percent, and CPU temperature in °C. Recovery alerts are optional. Scope records an alert when a rule changes state, rather than sending one for every metric sample.

08

CONNECT YOUR PROVIDERS.

For Webhook, enter a public HTTPS endpoint. For Discord, paste a Discord channel webhook URL. Switch on either provider and save; Scope stores provider secrets encrypted using your MFA_ENCRYPTION_SECRET. For email, choose Resend, Amazon SES, or SMTP in the same page, enter recipients, and enable Email.

Resend: Verify a sending domain, create a sending-only API key, then enter the key and sender address. Amazon SES: Verify the sender in your AWS region, then enter the region and an IAM access key pair, or use the backend's IAM role. Both options save directly from the dashboard without a redeploy.

SMTP: Add these values to the deployment .env on every backend replica. Port 587 uses STARTTLS by default; port 465 needs SCOPE_SMTP_SECURE=true.

# SMTP option in the deployment .env
SCOPE_SMTP_HOST=smtp.example.com
SCOPE_SMTP_PORT=587
SCOPE_SMTP_USER=your-smtp-user
SCOPE_SMTP_PASSWORD=your-smtp-password
[email protected]

After changing SMTP variables, run docker compose --env-file .env -f compose.yml up -d backend. Save settings before using the Test buttons; they send a real message.

09

OVERRIDE A DEVICE.

Open a device and select Alerts, or choose it on the Notifications page. Turn on custom settings to replace its thresholds and channel choices; turn custom settings off to inherit the global policy again. The global enable switch and provider switches always remain the master controls.

NEED DELIVERY DETAILS?

Read provider setup, alert payloads, and retry behavior in the notifications guide.

Notifications guide

NEED DEPLOYMENT DETAILS?

Read the agent guide for pairing, TLS, local development, and release details.

Agent installation guide