I just realized there is an HN post about Traceway. I didn't make it, and unfortunately my account is rate limited so I can't reply to everyone. I've written my response here
Website · Docs · Cloud · Discord
Traceway is an OpenTelemetry-native observability platform that combines logs, traces, metrics, session replay/RUM, exceptions, and AI tracing together. Point an OTLP exporter at it and you're in business. No Collector, no glue code, no per-language vendor SDK.
MIT licensed. No BSL. No "open core." Every feature is in the box. Self-host it for free, or run it on Traceway Cloud if you'd rather not babysit infra.
👋 Join the Traceway Community on Discord →
Chat with the team, shape the roadmap, get help, and meet other folks running Traceway in production.
This fork of tracewayapp/traceway replaces the public HTTP listener with a Tailscale tsnet node so the backend (SDK ingestion + dashboard + static SPA) is reachable only over a tailnet, and lands a batch of backend security fixes from an audit of backend/. All changes live on the security-and-tsnet branch.
Frontend / Go SDK / docs were intentionally out of scope for this fork.
Env mode now calls tsnet.Server.Listen (or ListenTLS if TSNET_HTTPS=true) instead of router.Run(:port). There is no public listener — SDKs and browsers must reach Traceway over the tailnet. Embedded mode (tracewaybackend.Run(WithPort(…))) still uses stdlib HTTP so tests / examples/embedded-backend-otel don't need a tailnet.
| Env var | Required | Default | Purpose |
|---|---|---|---|
TSNET_HOSTNAME |
yes | — | Node name registered with the tailnet (e.g. traceway) |
TSNET_AUTHKEY |
first start | — | Tailscale auth key; can be omitted after TSNET_DIR is persisted |
TSNET_DIR |
no | ./tsnet-state |
Directory where tsnet persists the node identity. Mount as a volume. |
TSNET_LISTEN_ADDR |
no | :80 (or :443 if HTTPS) |
Listen address inside the tailnet |
TSNET_HTTPS |
no | false |
If true, use srv.ListenTLS (LetsEncrypt via Tailscale MagicDNS) |
TSNET_LOGF |
no | quiet |
Set anything other than quiet to enable tsnet's internal logger |
PORTS is ignored (and logged) when tsnet mode is active. APP_BASE_URL should point at the tailnet hostname (http://traceway/) — invitation and password-reset emails use it.
Grouped by severity from the audit report. Every item below has been applied on this branch.
- C1 — Hardcoded JWT secret in embedded mode removed; new
WithJWTSecret(...)option, ephemeral random fallback for tests so embedded users never accidentally ship the public dev key. - C2 —
/api/reportgzip body capped: 32 MB raw + 256 MB decompressed. Project tokens ship to browsers via the JS SDK, so the prior unbounded gzip path was a public DoS.
- H1 + M6 — Source-map uploads sanitize
versionand filename to[A-Za-z0-9._-]{1,128},filepath.Base()on Filename, defense-in-depth containment check instorage/local.go. Prior code wrotefilepath.Join(basePath, "../../...")to disk. - H2 — OAuth no longer auto-links to an existing local account on email match; redirects with
email_in_use_signin_with_existing_methodso a provider returning an unverified email cannot take over a local-password account. - H3 — Password-reset + invitation tokens are now
crypto/rand32-byte values, SHA-256 hashed before DB storage; raw token only in the email. A DB leak can no longer replay outstanding tokens. - H4 — SMTP now does
STARTTLS(ports 587/25) or implicit TLS (465); refuses to send credentials if STARTTLS isn't available. - H5 — Email header values reject CR/LF; inviter name / org name / recipient sanitized before interpolation. Prior code allowed an attacker to inject
Bcc:via their own user name and exfiltrate invitation tokens. - H6 — All email lookups case-insensitive (
FindByEmail,IsUserMemberByEmail, invitation lookups); emails lowercased on insert;AcceptExistingUserusesstrings.EqualFold.
- M1 + L1 —
/loginand/forgot-passwordburn a constant-time bcrypt budget on the missing-user branch viaservices.DummyTimingWorkso response time no longer enumerates registered emails. - M2 — Global
MaxBodymiddleware caps non-telemetry JSON to 1 MB,/api/sourcemaps/uploadto 50 MB./api/reportand/api/otel/v1/*keep their own larger telemetry caps. - M3 —
OAUTH_SESSION_SECRETis required and must be ≥ 32 chars when an OAuth provider is configured. HMAC-derived auth + encryption keys (cookies are now encrypted, not just signed) — no more re-usingJWT_SECRETacross primitives. - M4 — Member-role updates re-validate the allowed set (
admin|user|readonly) in the controller body and inOrganizationRepository.UpdateUserRole;owneris rejected. - M5 + L2 — JWT carries a
tv(tokenVersion) claim;UseAppAuthverifies it against the liveusers.token_versioncolumn on every request; bumped on everyUpdatePassword. JWT TTL cut from 7d → 24h. New migrations:pg/0035_add_token_version_to_users.up.sql,sqlite/0010_add_token_version_to_users.up.sql. - M7 —
ProjectCachekeys tokens by SHA-256 hash instead of the raw value, removing the map-lookup timing oracle. - M9 — Monitoring
tracewayginforwarder no longer capturesRecordingBodyorRecordingHeader. The upstream Traceway no longer receivesAuthorizationbearer tokens or login / reset-password request bodies on error.
- L3 —
UpdatePasswordswitched tolit.UpdateNativewith an explicitUPDATE users SET password = …, token_version = token_version + 1 WHERE id = …so other columns can't be clobbered with zero values. - L4 —
/api/reportbind error no longer echoeserr.Error()to anonymous clients. Internal struct-field paths stay server-side. - L5 —
ProjectCache.AddProjectdeferred to post-commit via newmiddleware.AfterCommithook. A rolled-back registration can no longer leave a usable token in the in-memory cache. - I2 —
POSTGRES_SSLMODEdefault flipped fromdisabletorequire. Local dev needs an explicit opt-out. - I3 — Explicit
../ NUL reject in the SPA static handler.embed.FSalready enforces this; the explicit check guards against a future swap toos.DirFS.
- I1 —
modernc.org/sqlite v1.18.1is 3+ years stale. Needs a separate bump +govulncheckpass. - Source-map token lifecycle — token sanitization is done, but the static per-project token still never expires and has no per-project upload rate limit. Worth a follow-up.
- L4 broader sweep — only
/api/reportwas tightened. Authenticated dashboard endpoints still echo Gin bind errors verbatim (struct field paths + validator tags). Mild info disclosure for an already-authenticated user. - CORS
*on/api/report(M8 from the audit) — intentionally left as-is. Required for browser SDKs and now safer thanks to C2 + M2 caps. - Frontend / Go SDK / docs — entirely out of scope for this fork. The frontend still stores the JWT in
localStorage; M5's tokenVersion + 24h TTL is the partial mitigation. - Source-map upload UX — sanitization is strict (
[A-Za-z0-9._-]). Build tools that emit filenames with spaces or non-ASCII will now fail closed. Treat as a contract change for any CI that uploads source maps.
go build ./... # clean
go vet ./... # clean
go test ./app/services/... ./app/retention/... ./app/recordings/... # pass
app/repositories tests fail on this branch — but they also fail on main (pre-existing schema mismatch on span_id / session_id / parent_span_id columns). Not introduced by this fork.
- Logs — Structured, trace-linked, sub-second search. Native OTLP/HTTP ingest from any OTel SDK.
- Traces — End-to-end span waterfalls across every service. Click a log, jump to its span.
- Metrics — Host, runtime, and custom metrics. Any dimension, any chart, with custom widget groups.
- Exceptions — SHA-256 normalized stack traces grouped into ranked issues. Source-mapped (webpack, esbuild, Vite).
- Session Replay — Watch what the user did right before the error. Available for web (any JS framework) and Flutter.
- AI Observability — LLM cost, tokens, latency, and full conversations across providers (OpenRouter and any OTel-compatible AI gateway).
Plus: configurable alerts (Slack / GitHub / email / webhook), Apdex + Impact-Score endpoint ranking, multi-tenant orgs with role-based access, and a per-endpoint slow-threshold override.
| Enterprise (Datadog / New Relic) | DIY OSS stack (Prometheus + Loki + Tempo + ...) | Traceway | |
|---|---|---|---|
| Pricing | Per-event, per-host, per-seat | Free + ops time | Self-host free, fixed cloud tiers |
| Setup | Vendor SDK per language | Glue 6 tools together | docker compose up -d |
| License | Proprietary | Mixed (some BSL / open-core) | MIT — no asterisks |
| OTel | Wrapped in vendor SDK | OTel Collector required | Native OTLP/HTTP ingest |
| Replay + traces + AI | 3 separate products | Wire it yourself | One system, one trace ID |
git clone https://github.com/tracewayapp/traceway
cd traceway && docker compose up -d
# ✓ dashboard at http://localhostPoint any OTel SDK at http://localhost/api/otel/v1/traces (or /metrics, /logs) and traces start flowing. See the self-hosting docs for production deployment, TLS, and storage configuration.
Docker images are cryptographically signed. See DOCKER_SIGNATURES.md to verify images before deploying.
Run Traceway inside your Go process — no Docker, no external databases, SQLite under the hood:
go get github.com/tracewayapp/traceway/backendimport tracewaybackend "github.com/tracewayapp/traceway/backend"
func main() {
go tracewaybackend.Run(
tracewaybackend.WithPort(8082),
tracewaybackend.WithDefaultUser("admin@localhost.com", "admin"),
tracewaybackend.WithDefaultProject("My App", "go", "dev-token"),
)
// ... start your app, point its OTel exporter to http://localhost:8082/api/otel/v1/traces
}Open http://localhost:8082, log in, and hit your app to see traces appear. Full walkthrough in the embedded mode guide, or check the working example.
Traceway integrates with the tools you already use. Every integration ships traces, metrics, and logs over OTLP/HTTP — no proprietary SDK required.
View the full list in the documentation. Missing a framework? Open an issue to request it.
Gin |
Chi |
Fiber |
FastHTTP |
net/http |
Go Generic |
Node.js |
NestJS |
Hono |
Symfony |
Cloudflare |
OpenTelemetry |
Session Replay is included with every frontend integration — and with Flutter too.
Next.js |
React |
Vue |
Svelte |
jQuery |
JavaScript |
Flutter |
Android |
React Native |
OpenRouter |
Logs — trace-linked search![]() |
Span waterfall![]() |
Metrics — application dashboard![]() |
Exceptions — grouped & ranked![]() |
| Component | Technology |
|---|---|
| Backend | Go 1.25, Gin |
| Frontend | SvelteKit 2, Svelte 5, Tailwind CSS v4 |
| Telemetry DB | ClickHouse (standalone) or SQLite (embedded) |
| Relational DB | PostgreSQL (standalone) or SQLite (embedded) |
| Ingest | OTLP/HTTP (Protobuf + JSON) for traces, metrics, logs |
| Directory | Description |
|---|---|
backend/ |
Go/Gin API server — OTLP ingest, REST API, notifications, migrations |
frontend/ |
SvelteKit 2 dashboard SPA |
docs/ |
Documentation site (Nextra) |
examples/ |
Working examples — embedded mode and OTel-instrumented apps (Express, NestJS, Next.js, Hono) |
website/ |
Landing page |
| Tag | Purpose |
|---|---|
| (none) | SQLite storage — embedded mode, zero dependencies. This is the default. |
pgch |
ClickHouse + PostgreSQL storage — standalone server mode. |
localdist |
Embeds frontend from static/dist/ instead of static/frontend/. Used by traceway-cloud to inject billing UI. |
# Embedded mode (SQLite, default)
cd backend && go build ./cmd/traceway
# Standalone server (ClickHouse + PostgreSQL)
cd backend && go build -tags pgch ./cmd/traceway# SQLite tests (default, no tags needed)
cd backend && go test -v -count=1 ./app/repositories/
# ClickHouse + PostgreSQL tests (requires Docker)
./scripts/test-backend-pgch.sh
# OTEL trace converter tests (no DB required)
cd backend && go test -v -count=1 ./app/controllers/otelcontrollers/
# Update OTEL golden files after intentional converter changes
cd backend && go test -v -count=1 -args -update ./app/controllers/otelcontrollers/Full documentation at docs.tracewayapp.com:
- Client SDKs — OpenTelemetry, Go, Node.js, Python, and more
- Self-Hosting — Docker Compose and production deployment
- Concepts — How tracing, exception grouping, metrics, and alerts work
- Embedded Mode — Run Traceway inside your Go app
Traceway is built in the open, and the Discord community is where it happens. Come say hi — whether you're kicking the tires, running it in production, or just curious. We use it to:
- 🗣️ Talk through ideas — feature requests, integration asks, roadmap input
- 🛟 Help each other out — setup, OTel wiring, deployment questions
- 🚀 Show & tell — share what you're building and how you're using Traceway
- 🐛 Catch bugs early — report issues and get fast feedback from maintainers
- 👀 Get the inside scoop — sneak peeks at what's shipping next
Contributions are welcome — pull requests get reviewed and merged. If you're not sure where to start or want to discuss an idea first, open an issue or drop by the community Discord and we'll talk it through.
- Website
- Documentation
- Traceway Cloud — managed hosting (same MIT code, run by us)
- Community Discord — chat with the team and other users




