fix(layers): render bundled templates when the OAP template store is unreachable (#103) An unreachable template store used to block every layer-driven page: routes served nothing, so the Traces tab was empty with no explanation. The intent was to avoid rendering content that might not match the operator's OAP-stored configuration, but the trade was wrong — a blank page is not safer than a stated fallback. The unreachable case is also predictable rather than exotic. An OAP 10.x has no `/ui-management/templates*` endpoint at all: template management lives on the query port's GraphQL there, and Horizon implements only the OAP 11 REST protocol. The store is therefore unreachable for the whole life of such a deployment, which is what left Traces empty (apache/skywalking#13959). Layer resolution now falls back to the templates bundled in the release, while the connectivity banner keeps reporting the store as unreachable — the degrade is visible, not silent. An admin-disabled layer still blocks, which is a different and deliberate signal. What a fallback cannot supply is a template edit stored on OAP; those return as soon as a reachable store answers. Docs updated: `templates.mode: readonly` moves from required to recommended on OAP 10.x, since it states the deployment's reality up front and makes the admin surface honestly display-only. Also carries in-place doc corrections made against main: OAP 10 does have persistent template management (legacy query-port GraphQL, gated by SW_ENABLE_UPDATE_UI_TEMPLATE) — the gap is Horizon-side protocol support, not a missing OAP feature — plus the minor-specific limits Horizon's current queries impose (queryTrace duration needs 10.3+, findEndpoint duration 10.2+).
Horizon UI is the next-generation web UI for Apache SkyWalking — a config-driven, dark-dense, multi-layer observability front end built for feature parity with the legacy skywalking-booster-ui on the same OAP GraphQL query-protocol and MQE. It renders services, instances, endpoints, topology, traces, logs, alarms, and profiling across 44 instrumentation layers, ships an in-browser admin suite for runtime rules, RBAC, template management, and cluster status, and includes an optional bring-your-own-LLM AI assistant that answers questions from live OAP data using the same dashboard widgets. Dashboards are JSON templates: live OAP 11 deployments publish them to OAP, while readonly deployments render the bundled copies — new screens are configuration, not code.
/ai), or a new tab.ai: config block against any OpenAI-compatible endpoint or Amazon Bedrock, with the API key env-only and redacted from logs; access is RBAC-gated (ai:read), every data tool enforces the same read verbs the signed-in user already holds, and the assistant never changes configuration, rules, or dashboards.visibleWhen) — render a widget only when an MQE metric has a value or an entity attribute matches (e.g. language = JAVA, container_name = lifecycle).queryTraces on BanyanDB with inline spans, or queryBasicTraces on any backend), plus Zipkin traces in a separate tab when a layer enables both, with state/order/duration/tag filters, a duration distribution chart, and second-precision time windows..map files, resolved to original file/line/column/symbol with a source snippet.Horizon UI is a pnpm-workspaces monorepo:
apps/ui — the Vue 3 + TypeScript (strict) single-page app, built with Vite. State via Pinia, data via @tanstack/vue-query, charts via Apache ECharts (wrapped — never instantiated directly in a view), topology and flame graphs via D3, 3D via Three.js + TresJS, code editing via Monaco.apps/bff — a Fastify (Node) backend-for-frontend. It is the only tier that talks to OAP, shaping every reply for the SPA, owning timezone conversion, template sync, auth/RBAC, and the audit log.packages/api-client, packages/design-tokens, packages/templates — the shared GraphQL client, the canonical design tokens, and the bundled dashboard JSON.The BFF speaks three OAP contracts, all owned upstream and treated as fixed:
POST /graphql) — metrics, topology, traces, logs, alarms, browser errors.17128) — runtime rules/DSL, cluster status, metrics inspect, live debugging.The flow inside the BFF is one-directional — http → logic → client → OAP — and bundled templates are synced to OAP on first boot (bundled → local draft → push), after which the running UI renders only what OAP serves.
Prerequisites: Node (see package.json engines — >=22) and pnpm via Corepack (the repo pins the version through packageManager).
corepack enable
pnpm install
Run the dev servers — Vite serves the UI on :9091 and proxies /api to the BFF on :8081:
pnpm dev # run UI + BFF together pnpm dev:ui # UI only (Vite on :9091) pnpm dev:bff # BFF only (:8081, NODE_ENV=development, tsx watch)
Quality gates and build:
pnpm type-check # TypeScript strict, all packages pnpm lint pnpm test:unit # vitest + jsdom pnpm build # build all packages pnpm package # produce the distributable dist/ (BFF dist/server.js + static UI) pnpm start # run the packaged server (HORIZON_CONFIG=./horizon.yaml)
License headers and dependency licenses are enforced in CI via skywalking-eyes — run pnpm license:check (or pnpm license:fix) before pushing.
A multi-stage, multi-arch image (linux/amd64, linux/arm64) ships the BFF plus the static UI on node Alpine, runs as a non-root horizon user, exposes port 8081, and mounts /data for state files. The Dockerfile builds dist/ from source inside the image (no host pre-step); images are published to GHCR and Docker Hub per release.
docker build -t horizon-ui:local . docker run --rm -p 8081:8081 -v "$PWD/horizon.yaml:/app/horizon.yaml:ro" horizon-ui:local
See docs/setup/container-image.md for image tags, env vars, mounting horizon.yaml, and a Kubernetes example.
Horizon UI is configured by a single horizon.yaml (hot-reloaded, with ${VAR} environment-variable interpolation) — see horizon.yaml. Key sections:
server — host / port.oap — queryUrl, adminUrl, zipkinUrl, timeoutMs, and optional outbound basic-auth.auth — backend local or ldap (with LDAP bind / user-filter / group-mapping and an optional audited break-glass local admin).rbac — four built-in roles (viewer / maintainer / operator / admin) over fine-grained, verb-namespaced permissions (e.g. dashboard:write, rule:write:structural, source-map:write).templates — live (default: bundled templates seed through OAP 11's REST API and stay editable) or readonly (render from the local bundle; required for OAP 10 because Horizon does not consume its legacy GraphQL template API).ai — the AI assistant: enabled (off by default), provider (openai-compatible or bedrock), model, base URL, and an env-only API key.performance — how hard the BFF fans metric queries out to OAP (per-route bulk sizes and concurrency) plus protective caps (topology render valve, per-request record limits).query — load caps: landingServiceCap (how many top services a layer landing fetches metrics for) and overviewTopN (the Overview KPI rollup window).session, audit (audit-log toggle + file), debugLog (the outbound OAP wire log), sourceMaps (browser-error source-map cache), and layers.excluded — the audit and wire-log files default under /data/* in the container.Local user passwords are argon2-hashed; generate a hash with the BFF CLI. Set session.cookieSecure: true when running behind HTTPS.
Operator-focused documentation (setup, OAP compatibility, access control, customization, components, and operate) lives in this repo under docs/ and is rendered on the SkyWalking website. Start with the Quick Start, then OAP Connection and Auth/RBAC.
Contributions are welcome. Horizon UI is a greenfield rewrite that tracks the OAP GraphQL query-protocol and MQE — backend contracts are fixed and owned by apache/skywalking. Read CLAUDE.md for the project's working principles (correctness first, validate against a live OAP, TypeScript strict, charts wrapped, density beats whitespace), keep CHANGELOG.md current, and run the type-check / lint / unit-test / license-header gates before opening a PR.
Licensed under the Apache License 2.0 — see LICENSE and NOTICE.
Apache SkyWalking, SkyWalking, and the Apache feather logo are trademarks of The Apache Software Foundation.