Contributor Guide

Contributing to SigNoz

Single authoritative reference — architecture, dev environment, Go standards, frontend patterns, and testing. Everything in one place.

Go 1.24 React 18 + TypeScript OpenTelemetry ClickHouse Python Integration Tests

System Architecture Overview

SigNoz is an OpenTelemetry-native observability platform. Telemetry flows from instrumented apps → OTel Collector → ClickHouse → Go backend → React frontend. All backend services are packaged as a single binary.

High-Level System Architecture
APPLICATIONS Service A Service B Service C OTel SDK Traces · Metrics · Logs OTLP OTEL COLLECTOR Receivers (OTLP/Jaeger) Processors (batch/enrich) Exporters → ClickHouse signoz-otel-collector v0.129.4 writes CLICKHOUSE signoz_traces signoz_metrics signoz_logs attributes_metadata columnar OLAP · TTL · replicated SQL SIGNOZ BINARY (Go 1.24) API Server (gorilla/mux) :8080 public · :8085 private · v1–v5 endpoints Query Service QueryBuilder · ClickHouseReader · Querier Ruler alert eval AlertManager notify OpAMP log pipelines Frontend ReactJS SPA SQLite (rules · orgs · users) Browser / User

Core Components

🗄️
ClickHouse
Columnar OLAP DB. All traces, metrics, logs. Configurable TTL per signal.
📡
OTel Collector
Custom-built collector. Receives OTLP/Jaeger/Zipkin, enriches, writes to ClickHouse.
⚙️
Query Service (Go)
Core backend. API handling, SQL via QueryBuilder, caching, alert eval.
🔔
Ruler + AlertManager
Ruler evaluates rules on schedule. AlertManager deduplicates and routes.
🔧
OpAMP
Dynamically configures log collection pipelines in the OTel Collector.
🖥️
Frontend (React)
TypeScript SPA. Redux + React Query. Traces, logs, metrics, alerts.

Frontend Architecture

React 18 TypeScript SPA built with Webpack. Ant Design components, Redux for global state, React Query for server data. All pages lazy-loaded. RBAC enforced at route and component level.

Provider Hierarchy & State Ownership
PROVIDER HIERARCHY (index.tsx) HelmetProvider (document head) ThemeProvider + TimezoneProvider QueryClientProvider (React Query) Redux Provider AppProvider (user · license · flags) AppRoutes + PrivateRoute (RBAC) ADMIN · EDITOR · VIEWER enforced per route + component STATE MANAGEMENT Redux React Query Context API global UI state server data + cache dashboard · QB store/ QueryClientProvider providers/ version, app flags telemetry, API calls dashboard, query LAZY-LOADED PAGES Services Traces Expl. Logs Expl. Dashboards Alerts Metrics Infra Settings Webpack 5 · Dev HMR on :3301 · Prod: code-split, gzip, Sentry source maps

Backend Architecture

Single Go 1.24 binary. API server exposes :8080 (public) and :8085 (private). The provider pattern is used throughout — each subsystem is a typed interface with pluggable implementations wired via pkg/signoz.

Data Flow Pipeline

Ingestion → Storage → Query → Visualise
App OTel SDK OTLP OTel Collector receive · process · export write ClickHouse columnar · TTL · replicated SQL Query Service API v1–v5 · cache · builder JSON Frontend React · dashboards · alerts

Repository Structure

signoz/
├── frontend/                   # React 18 TypeScript SPA
│   ├── src/
│   │   ├── AppRoutes/          # Routes, PrivateRoute, RBAC guards
│   │   ├── api/                # API client functions
│   │   ├── components/         # Reusable UI components
│   │   ├── container/          # AppLayout, SideNav, TopNav
│   │   ├── providers/          # Context: App, Dashboard, QueryBuilder
│   │   └── store/              # Redux store
│   └── src/container/OnboardingV2Container/onboarding-configs/
│       └── onboarding-config-with-links.json  # New Source / Get Started config
│
├── pkg/query-service/          # OSS Go backend
│   └── app/
│       ├── clickhouseReader/   # All ClickHouse reads (interfaces.Reader)
│       ├── http_handler.go     # HTTP route handlers
│       └── server.go           # Server struct, :8080/:8085
│
├── pkg/                        # Shared Go packages (OSS)
│   ├── apiserver/signozapiserver/  # Route registration, OpenAPI defs
│   ├── errors/                 # Structured error package
│   ├── factory/                # Provider + Service lifecycle
│   ├── flagger/                # Feature flag system (OpenFeature)
│   ├── modules/                # Business logic modules (user, session, org)
│   ├── signoz/                 # IoC container — wires all providers
│   ├── sqlstore/               # SQL abstraction (Bun ORM, PostgreSQL/SQLite)
│   └── types/                  # Shared domain types
│
├── ee/query-service/           # Enterprise-only features (never import from pkg/)
├── .devenv/docker/clickhouse/  # Local dev ClickHouse compose
├── deploy/docker/              # Docker Compose (standalone + HA)
├── tests/integration/          # Python/pytest integration tests
├── docs/api/openapi.yml        # Auto-generated OpenAPI spec
├── docs/contributing/         # This guide's source files
└── go.mod                      # Go 1.24.0, 300+ deps

Development Environment Setup

SigNoz has three main components you need to run locally: ClickHouse, Backend, and Frontend. Run make help to see all available Make commands.

Prerequisites

ToolVersionPurpose
Gosee go.modBackend development
Node.jssee frontend/.nvmrcFrontend development
Yarn1.x classicFrontend package manager
Docker + Compose20.10+ / v2+ClickHouse + Postgres locally
git clone https://github.com/SigNoz/signoz.git && cd signoz

1 — Start ClickHouse

# Starts ClickHouse single-shard single-replica + Zookeeper + schema migrations
make devenv-clickhouse

# Start OTel Collector (listens :4317 gRPC, :4318 HTTP)
make devenv-signoz-otel-collector

# Or start both at once
make devenv-up

# Verify
curl http://localhost:8123/ping          # → Ok.
curl http://localhost:13133             # → OTel Collector health

2 — Run the Backend

# OSS build (runs on :8080)
make go-run-community

# Verify
curl http://localhost:8080/api/v1/health   # → {"status":"ok"}

3 — Run the Frontend

cd frontend
yarn install
# Create .env
echo "VITE_FRONTEND_API_ENDPOINT=http://localhost:8080" > .env
yarn dev    # starts on :3301 with HMR, auto-rebuilds on change

Sending Test Data

# OTLP gRPC: localhost:4317 | OTLP HTTP: localhost:4318
curl -X POST http://localhost:4318/v1/traces \
  -H "Content-Type: application/json" \
  -d '{"resourceSpans":[{"resource":{"attributes":[{"key":"service.name","value":{"stringValue":"test-service"}}]},"scopeSpans":[{"spans":[{"traceId":"12345678901234567890123456789012","spanId":"1234567890123456","name":"test-span","startTimeUnixNano":"1609459200000000000","endTimeUnixNano":"1609459201000000000"}]}]}]}'

Deployment Options

ModeConfigUse CaseHA
Docker Composedeploy/docker/docker-compose.yamlSingle-node / dev
Docker Compose HAdeploy/docker/docker-compose.ha.yaml3-node CH + 3-node ZK
Docker Swarmdeploy/docker-swarm/docker-compose.yamlMulti-node orchestratedPartial
KubernetesHelm charts (SigNoz/charts)Cloud-native
Quick installdeploy/install.shOne-command Linux/macOS

Go Backend — Contributing Standards

📖 Before contributing to the backend, read all three primary style guides: Effective Go, Code Review Comments, and the Google Go Style Guide. These are effectively enforced in review.
📦 Packages
Naming, layout, and conventions for pkg/ packages
🧩 Abstractions
When to introduce new types and interfaces
🚨 Errors
Structured error handling with pkg/errors
🔌 Provider
Dependency injection pattern
⚡ Service
Managed lifecycle with factory.Service
🌐 Handler
HTTP handler patterns + OpenAPI
🔗 Endpoint
RESTful endpoint design
🚩 Flagger
Feature flag system (OpenFeature)
🗃️ SQL
Database patterns and migrations
🔍 Query Range v5
Design principles and invariants

Packages

All shared Go code lives under pkg/. Each package represents a distinct domain concept with a clear public interface.

RuleDetail
NamingShort, lowercase, single-word: querier, authz, cache. No underscores or camelCase. Domain-specific — not generic (util, helpers, common are banned).
When to createDistinct domain concept used by 2+ other packages. Do NOT create for code used in only one place — keep it local.
Interface fileFile matching the package name (e.g. cache.go in pkg/cache/) defines the public interface. Keep implementation details out.
ImplementationsPut each implementation in its own sub-package: memorycache/, rediscache/.
Test helpersPut in {pkg}test/ sub-package (e.g. cachetest/). Never pollute the main package with test-only code.
Circular importsNever. Extract shared types into pkg/types/.
OSS vs EEOSS code → pkg/. Enterprise-only → ee/. Never import ee/ from pkg/.

Import Groups

import (
    // 1. Standard library
    "fmt"
    "net/http"

    // 2. External dependencies
    "github.com/gorilla/mux"

    // 3. Internal
    "github.com/SigNoz/signoz/pkg/errors"
)

Abstractions

Every exported type, interface, or wrapper is a permanent commitment. Before introducing one, answer four questions in your PR description:

  1. What already exists? Name the specific type/function that covers this today.
  2. What does the new abstraction add? Name the concrete capability. "Cleaner" or "more reusable" are insufficient.
  3. What does it drop? List what it cannot represent. Every gap must be justified or error-handled.
  4. Who consumes it? List all call sites. One producer + one consumer = you need a function, not a type.
RuleDetail
Functions over typesIf it has one input and one output, write a function — not a struct. func ConvertConfig(src ExternalConfig) (InternalConfig, error) beats a ConfigAdapter struct.
Don't duplicate external typesOperate on library output directly. A partial copy silently loses data, drifts, and doubles code surface.
Never silently discard inputUnrecognised enum values, missing cases — always return an error. default: return nil, fmt.Errorf("unsupported %T: %v", v, v)
No lossy methodsDon't add methods that strip meaning. Write standalone functions that operate on the full struct instead.
Discover interfacesNever define an interface before you have ≥2 concrete implementations. Exception: testing mocks, which go in the consuming package.
Wrappers must add semanticsA wrapper is justified only when it adds invariants/validation the underlying type doesn't carry. Ask: what does it guarantee? If nothing — don't wrap.

When a new type IS warranted

Errors

SigNoz has its own structured error package at pkg/errors. Always use it instead of the standard library.

// Instead of errors.New() or fmt.Errorf():
errors.New(typ, code, message)
errors.Newf(typ, code, message, args...)
errors.Wrapf(err, typ, code, message)   // adds context, preserves original
ConceptDetail
TypCategorises errors, loosely coupled to HTTP/gRPC status codes. Defined in pkg/errors/type.go: TypeInvalidInput, TypeNotFound, TypeForbidden, etc. Cannot be declared outside the package.
CodeGranular categorisation within a type. Create with errors.MustNewCode("thing_not_found"). Must match ^[a-z_]+$ or panics. Always assign specific codes — avoid catch-all codes.
WrappingUse errors.Wrapf to add context while preserving the original error as it travels up the call stack.
CheckingUse errors.As(err, errors.TypeNotFound) to branch on error type. Handlers derive HTTP status codes from the type automatically.
⚠️ Think about error handling as you write code — not as an afterthought. It is the responsibility of each function to return well-defined errors that accurately describe what went wrong.

Provider Pattern

Providers are the dependency injection mechanism. They adapt external services and deliver functionality to the application. Think of them as typed, configurable adapters wired together in pkg/signoz.

Anatomy of a provider

FilePurpose
pkg/<name>/<name>.goPublic interface — other packages import this.
pkg/<name>/config.goConfiguration, implements factory.Config.
pkg/<name>/<impl><name>/provider.goConcrete implementation with NewProvider returning factory.Provider.
pkg/<name>/<name>test.goMock for use in unit tests by dependent packages.

Wiring (3 steps in pkg/signoz)

// 1. Add config to pkg/signoz/config.go
type Config struct {
    MyProvider myprovider.Config `mapstructure:"myprovider"`
}

// 2. Register factory in pkg/signoz/provider.go
func NewMyProviderFactories() factory.NamedMap[...] {
    return factory.MustNewNamedMap(myproviderone.NewFactory())
}

// 3. Instantiate in pkg/signoz/signoz.go SigNoz struct
myprovider, err := myproviderone.New(ctx, settings, config.MyProvider, "one/two")
💡 Always create a provider interface even with a single implementation. This makes it trivial to add new implementations or mocks later.

Service Lifecycle

A service is a component with a managed lifecycle: it starts, runs for the application's lifetime, and stops gracefully. Services are distinct from providers — a provider adapts an external dependency; a service has its own lifecycle tied to the application.

The factory.Service interface

type Service interface {
    Start(context.Context) error  // MUST BLOCK until stopped or error
    Stop(context.Context) error   // causes Start to unblock
}

Shutdown coordination — always use stopC

// Constructor
stopC: make(chan struct{})

// Start: block
<-provider.stopC

// Stop: unblock
close(provider.stopC)
ShapePatternExample
IdleStart blocks on <-stopC. Stop closes channel, optionally cleans up.JWT tokenizer — responds to calls, no periodic work
ScheduledStart runs a ticker loop with select { case <-stopC / case <-ticker.C }. Errors logged, not returned (would kill app).Opaque tokenizer GC — periodic gc + flush

Wiring to the registry

registry, err := factory.NewRegistry(
    instrumentation.Logger(),
    factory.NewNamedService(factory.MustNewName("myservice"), myService),
    // ... more services
)
// Registry calls Start on all services concurrently.
// Any Start error triggers application shutdown.
// Stop is called on all services concurrently at shutdown.
⚠️ Only return an error from Start if the failure is unrecoverable. Log and continue for transient errors in polling loops — returning an error shuts down the entire application.

HTTP Handlers

Handlers are thin adapters — they decode HTTP requests, call the appropriate module, and return structured responses. Business logic belongs in modules (pkg/modules/user, pkg/modules/session, etc.), not in handlers.

Typical handler implementation

func (h *handler) CreateThing(rw http.ResponseWriter, req *http.Request) {
    claims, err := authtypes.ClaimsFromContext(req.Context())
    if err != nil { render.Error(rw, err); return }

    var in types.PostableThing
    if err := binding.JSON.BindBody(req.Body, &in); err != nil {
        render.Error(rw, err); return
    }

    out, err := h.module.CreateThing(req.Context(), claims.OrgID, &in)
    if err != nil { render.Error(rw, err); return }

    render.Success(rw, http.StatusCreated, out)
}

Registering a route in signozapiserver

if err := router.Handle("/api/v1/things", handler.New(
    provider.authZ.AdminAccess(provider.thingHandler.CreateThing),
    handler.OpenAPIDef{
        ID:                "CreateThing",
        Tags:              []string{"things"},
        Summary:           "Create thing",
        Description:       "This endpoint creates a thing",
        Request:           new(types.PostableThing),
        RequestContentType: "application/json",
        Response:          new(types.GettableThing),
        SuccessStatusCode: http.StatusCreated,
        ErrorStatusCodes:  []int{http.StatusBadRequest, http.StatusConflict},
        SecuritySchemes:   newSecuritySchemes(types.RoleAdmin),
    },
)).Methods(http.MethodPost).GetError(); err != nil {
    return err
}

// After adding a new endpoint, regenerate the OpenAPI spec:
// go run cmd/enterprise/*.go generate openapi

OpenAPI schema tags

TagWhen to use
required:"true"The JSON key must be present (even if zero value). Different from non-null.
nullable:"true"The value can be null. Critical for slice/map fields — Go's nil serializes to null. Mark nullable if Go code can return nil; otherwise initialize to empty slice.
Enum() []anyImplement on any type with a fixed set of values. Generates enum constraint in the spec. Required for all enum types.
JSONSchema()Implement jsonschema.Exposer for types needing a completely custom schema (e.g. accepts string OR number).

Endpoint Design

Think of endpoint structure like navigating a file system. Resources should be pluralized and hierarchical.

POST   /v1/organizations          # Create
GET    /v1/organizations/:id       # Get by ID
PUT    /v1/organizations/:id       # Update
DELETE /v1/organizations/:id       # Delete
GET    /v1/organizations/:id/users # Nested resource
GET    /v1/organizations/me/users  # me = resolved via auth context
💡 me endpoints are symlinks — they resolve to the authenticated user's actual org/resource via the auth mechanism. When in doubt, diagram the resource relationships as a file system tree before coding.

Feature Flags (Flagger)

Flagger is built on OpenFeature (CNCF). Three components: Registry (pkg/flagger/registry.go) — all available flags and defaults. Flagger (pkg/flagger/flagger.go) — consumer interface. Providers (pkg/flagger/<provider>flagger/) — supply override values.

Adding a new flag (2 steps)

// Step 1: Register in pkg/flagger/registry.go
var FeatureMyNewFeature = featuretypes.MustNewName("my_new_feature")

// Add to MustNewRegistry():
&featuretypes.Feature{
    Name:           FeatureMyNewFeature,
    Kind:           featuretypes.KindBoolean,
    Stage:          featuretypes.StageStable,
    Description:    "Controls whether my new feature is enabled",
    DefaultVariant: featuretypes.MustNewName("disabled"),
    Variants:       featuretypes.NewBooleanVariants(),
}

// Step 2 (optional): Override in config
// flagger.config.boolean.my_new_feature: true

Evaluating flags

evalCtx := featuretypes.NewFlaggerEvaluationContext(orgID)

// With error handling
enabled, err := flagger.Boolean(ctx, flagger.FeatureMyNewFeature, evalCtx)

// Non-critical — returns false on error and logs
if flagger.BooleanOrEmpty(ctx, flagger.FeatureMyNewFeature, evalCtx) { ... }
ℹ️ Feature names must match ^[a-z_]+$. Export the name variable (e.g. FeatureMyNewFeature) for type-safe usage across packages. Providers are evaluated in order; the first non-default value wins.

SQL & Database Patterns

SigNoz uses Bun ORM (pkg/sqlstore) against PostgreSQL or SQLite. The schema follows a star schema with organizations as the central entity — all other tables link to it via org_id foreign key.

type Thing struct {
    bun.BaseModel
    ID            types.Identifiable  `bun:",embed"`
    SomeColumn    string              `bun:"some_column"`
    TimeAuditable types.TimeAuditable `bun:",embed"`
    OrgID         string              `bun:"org_id"`
}

// Read
err := sqlstore.BunDBCtx(ctx).NewSelect().Model(thing).Where("id = ?", id).Scan(ctx)

// Transactions across multiple operations
err := sqlstore.RunInTxCtx(ctx, func(ctx context.Context) error { ... })

Migration rules

Query Range v5 — Design Principles

Core principle: The user speaks OpenTelemetry. The storage speaks ClickHouse. The system translates between them. These two worlds must never leak into each other.

The Central Type: TelemetryFieldKey

Every filter, aggregation, group-by, order-by, and select is expressed in terms of TelemetryFieldKey. It is identified by three dimensions: Name, FieldContext (resource / attribute / span / log / body), and FieldDataType.

The Abstraction Stack (must never be bypassed)

StatementBuilder          ← orchestrates into executable SQL
  ├── AggExprRewriter     ← rewrites aggregation expressions
  ├── ConditionBuilder    ← builds WHERE predicates
  └── FieldMapper         ← TelemetryFieldKey → ClickHouse column

Inviolable Rules

1User-facing types never contain ClickHouse column names or SQL fragments.

2Field-to-column translation happens only in FieldMapper.

3Normalization happens once at the API boundary (TelemetryFieldKey.Normalize() during JSON unmarshal). Never re-parse or re-normalize downstream.

4Historical aliases in fieldContexts (tagattribute, spanfieldspan) must never be removed — existing saved queries depend on them.

5Formula evaluation (A + B, A / B) stays in Go — do not push into ClickHouse JOINs.

6Zero-defaulting is aggregation-type-dependent. Only additive aggregations (count, sum, rate) default to zero. Statistical aggregations (avg, min, max, percentiles) must show gaps.

7Positive operators (=, IN) implicitly assert field existence. Negative operators (!=, NOT IN) do not — they include records where the field doesn't exist.

8Post-processing functions (ewma, fillZero, timeShift, etc.) operate on Go result sets, not in SQL.

9All user-facing types reject unknown JSON fields with Levenshtein-based suggestions. Implement custom UnmarshalJSON with DisallowUnknownFields.

10Query names must be unique within a composite query. Multi-aggregation refs use indexed (A.0) or aliased (A.total) notation.

11Validation rules are gated by request type (time_series, scalar, raw, etc.).

12The four-layer abstraction stack must not be bypassed or flattened.

Finding Issues to Work On

🟢
Good First Issues
Label: good first issue — well-scoped. Best entry point.
🐛
Bug Reports
Use Bug Report template. Include repro steps, version, expected vs actual.
Feature Requests
Use Feature Request template. Include use case and open questions.
🔒
Security Issues
Follow Security Policy. Never open public issues for vulnerabilities.

Pull Request Workflow

1
Branch off main
git checkout main && git pull && git checkout -b feat/my-feature
2
Keep PRs focused

One logical change per PR. Unrelated changes → separate PR.

3
Tests pass locally
yarn test && go test ./...
4
Submit and respond

No merge conflicts with main. Address CI failures. Respond to review comments within 1–2 business days.

For large contributions, split: (1) Structure — interfaces, types, configs. (2) Core implementation. (3) Docs + e2e tests.

Commit Convention

SigNoz follows Conventional Commits.

type(scope): short description

# Optional body (wrap at 72 chars)
Closes #1234
TypeWhenExample
featNew featurefeat(logs): add regex filter
fixBug fixfix(traces): correct span duration
docsDocs onlydocs(contributing): add Go guide
refactorNo functional changerefactor(query-service): extract builder
testTests onlytest(clickhouse): add reader unit tests
choreBuild, deps, CIchore(deps): upgrade react-query

Testing Guide

Frontend

yarn test                           # all unit tests
yarn test:coverage                  # with coverage report
yarn test --watch src/components/X  # watch mode

Co-locate tests: ComponentName.test.tsx or __tests__/. Use React Testing Library — test behaviour, not implementation.

Backend

go test ./...                        # all unit tests
go test -v ./pkg/query-service/...  # verbose
go test -run TestQueryBuilder ./... # specific test
golangci-lint run ./...              # lint

Integration Tests

Python/pytest framework in tests/integration/ runs against real ClickHouse, PostgreSQL, and Zookeeper via testcontainers. Wiremock provides service doubles.

1
Install
cd tests/integration && uv sync
2
Start environment (~4 min first time)
uv run pytest --basetemp=./tmp/ -vv --reuse src/bootstrap/setup.py::test_setup
3
Tear down
uv run pytest --basetemp=./tmp/ -vv --teardown -s src/bootstrap/setup.py::test_teardown

Running tests

uv run pytest --basetemp=./tmp/ -vv --reuse src/              # all
uv run pytest --basetemp=./tmp/ -vv --reuse src/querier/      # suite
uv run pytest --basetemp=./tmp/ -vv --reuse src/auth/01_register.py::test_register  # single

Key rules

Frontend Code Standards

AreaStandard
LanguageTypeScript strict: true. No implicit any.
ComponentsFunctional + hooks only. No new class components.
StateServer state → React Query. Global UI → Redux. Feature state → Context API.
StylingAnt Design first. Styled Components for theme overrides. SCSS for legacy pages.
ImportsUse TS path aliases. No deep relative imports.
Code splittingAll page-level components must be lazy-loaded via Loadable.
RBACNew routes must specify roles in route config.
i18nUser-visible strings in public/locales/en/. Use useTranslation.

Adding a New Data Source (Onboarding)

The onboarding "New Source / Get Started" flow is configured via JSON at frontend/src/container/OnboardingV2Container/onboarding-configs/onboarding-config-with-links.json.

Required fields per data source object

FieldTypeDescription
dataSourcestringUnique kebab-case identifier (e.g. "aws-ec2")
labelstringDisplay name (e.g. "AWS EC2")
tagsstring[]Category tags: AWS, Azure, database, logs, etc.
modulestringDestination after onboarding: apm, logs, metrics, dashboards, infra-monitoring-hosts, etc.
imgUrlstringPath to SVG logo: "/Logos/ec2.svg" — SVG required, optimize with SVGOMG before committing.

Checklist before submitting

Other Repositories

📦 SigNoz/charts — Helm charts 📊 SigNoz/dashboards — Dashboard templates 🔌 SigNoz/signoz-mcp-server — MCP server 🌐 SigNoz/signoz-web — Docs website

Each repo has its own CONTRIBUTING.md. Always check it before submitting PRs.

Community & Getting Help

ChannelPurpose
#contributing (Slack)General contribution questions
#contributing-frontend (Slack)Frontend-specific help
GitHub DiscussionsDesign proposals, architecture decisions
GitHub IssuesBug reports, feature requests
Security PolicyPrivate vulnerability disclosure only