Skip to content
repoless

Beacon reference

Multi-tenant observability with deploy DNA.

Beacon ingests gzip-compressed JSON log batches, indexes them in D1, and serves real-time search, auto-grouped issues, and alert rules — with every line tagged by the deploy revision that produced it.

Log ingest

Beacon accepts logs via a single HTTP endpoint. The path is /v1/logs/ingest on beacon.repoless.com (or any of your tenant subdomains).

  • Auth: bearer host token lwh_*. Mint one from Admin → Hosts.
  • Body: newline-delimited JSON (NDJSON), gzipped. One log line per JSON object.
  • Size cap: decompression is capped at 32 MiB to prevent gzip bombs. Batch your sends below that.
  • Content-Encoding: must be gzip.
shell
$ printf '{"service":"api","msg":"hi","level":"info"}\n' \
  | gzip \
  | curl -X POST https://beacon.repoless.com/v1/logs/ingest \
      -H "Authorization: Bearer lwh_•••••" \
      -H "Content-Encoding: gzip" \
      -H "Content-Type: application/x-ndjson" \
      --data-binary @-

Recognized fields

  • service — required. The service identity (see below).
  • project — optional but recommended. Groups services in the UI.
  • resource — optional. Container name or host name; helps when one service has multiple replicas.
  • service_type — optional. Annotates as app, db, or other.
  • level — info / warn / error / debug. Drives the issue grouping pipeline.
  • msg — the log line.
  • deploywatch.revision — set automatically by the Pier agent. Manually settable for non-Pier deploys.
  • Any additional fields are stored and searchable as facets.

Drop-in Vector config

Most teams use Vector to tail Docker logs and forward them to Beacon. The provided deploy/vector/vector.yaml tags every container based on Compose labels and container-name role prefix:

service = <coolify.serviceName>[-<role>][-pr-<id>]
# web-bgyb…            → narmypayroll-web
# worker-bgyb…         → narmypayroll-worker
# web-bgyb…-pr-163     → narmypayroll-web-pr-163
# i85ic49zy14c…        → platform-redis  (fallback)

Service identity

Services are derived, not enrolled. The first log line from a new service value creates a service row automatically; nothing to pre-configure. This means Pier can spin up an app, deploy it, and have its logs streaming in Beacon within ~1 second of the first stdout.

Full-text search lives at /search. Filter by project, service, level, and time range. Beacon uses D1 FTS indexes — sub-second for the typical query.

  • Free text: matches against msg and structured fields.
  • Facet filters: click any value in a result to add it to the filter set.
  • Revision filter: rev:c0a3f02 isolates logs from a single deploy — typically deep-linked from a Pier revision card.

Saved queries

Promote any search to a saved query for quick recall. Saved queries carry their filter set, time window, and grouping. They live under Account → Saved searches and can be referenced from alert rules.

Collections

Collections are user-defined groupings of services that often need to be watched together — a domain (auth flow, checkout, search), a microservice family, a customer-specific subset. Each collection appears on the dashboard with rollup line counts and the most recent activity. CRUD from /collections.

Aliases & display names

Raw container names are useful for debugging but ugly in dashboards. Beacon supports two layers of friendlier naming:

  • App aliases: map raw container names to a stable service identifier (handles renames during container churn).
  • Display names: a human-friendly label shown across the UI without changing the underlying service identity.

Admins configure both at /admin/labels. Changes are instant — no log re-ingest needed.

Auto-grouped issues

Error-level logs are normalized and grouped into issues automatically. Beacon strips noisy variants (timestamps, request IDs, memory addresses) before computing a signature so semantically identical errors collapse into one row.

Each issue tracks:

  • Total occurrence count and last-seen time
  • The deploy revision it first appeared in
  • A frequency sparkline over the last 24h
  • A few representative log lines for context

Click an issue to open its detail page with full occurrence history and quick links to the revision that introduced it.

Alert rules

Alerts fire when a search query crosses a threshold. Rule types:

  • Error pattern: a new issue appears that matches a regex on msg.
  • Log volume: count of matching lines in a rolling window crosses a threshold.
  • Uptime: no logs from a service in N minutes (proxy for "the service is dead").

Each rule routes to one or more channels — Slack, Discord, email, or HTTP webhook — defined in the shared notification channels table. Per-rule cooldowns prevent floods.

Retention

Logs live in D1 until the retention worker deletes them. Default is 30 days. Configure under /admin/settings; the retention worker runs on a cron and prunes by ingested_at.

Manual cleanup

Admins can trigger an out-of-band retention sweep from the settings page — useful right after lowering the retention window.

Team & RBAC

  • Members: admins add or remove people who can sign in. Members start with no log access.
  • Project access: deny-by-default. Grant a member access to a specific project; they see only that project's services, issues, and alerts.
  • Roles: admin manages team + settings; member sees logs they have access to.

Same RBAC table (user_project_access) is shared with Pier — invite once, both products honor it.

Devices & sessions

Every sign-in mints a session with device metadata (browser, OS, IP prefix). Manage active sessions from Account → Devices. Sign out a device remotely; it will need to log in again.

Personal settings

  • Timezone: per-user. Drives every timestamp display across the app.
  • Default time window: when you open search, this is the default range.

Rate limits

Auth endpoints (/api/auth/*) are per-IP rate-limited to prevent OTP brute-forcing. Ingest endpoints rely on bearer-token validation and don't per-IP limit; instead the 32 MiB body cap and gzip-bomb guards constrain throughput.

Multi-tenancy

Beacon is multi-tenant from the ground up. Every row in every table carries a tenant_id and queries scope through a withTenant() wrapper that enforces it. Tenants get a wildcard subdomain (<slug>.beacon.repoless.com); Universal SSL on the proxied wildcard covers all of them.