Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

918 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Jul.IA

Jul.IA logo

Status CI Go Version License Codecov PRs Welcome

Jul.IA is a self-contained edge and protocol gateway written in Go and configured through TOML. It combines reverse proxying, load balancing, static serving, FastCGI/uWSGI, HTTP and gRPC gateway behavior, optional L4 proxying, TLS/mTLS, policy, observability, configuration lifecycle, and an embedded operations Console in a single static, dependency-free binary.

  • Binary / module / service name: jul
  • Product name: Jul.IA
  • Language: Go 1.26.5
  • License: AGPL-3.0

Current direction and sequencing are governed by the combined repository audit and the master programme. The durable product direction remains in the vision, roadmap, engineering specs, and ADRs. The permanent OSS/open-core boundary is defined in ADR 0012.

New to HTTP, proxies, TLS, caching, or observability? The concepts appendix walks through how a request travels through modern edge infrastructure from first principles.


Who is Jul.IA for?

Jul.IA is for: solo operators and small infrastructure teams who want NGINX-grade routing and reverse proxying — plus protocol gateway capabilities (gRPC transcoding, L4 TCP/UDP proxying) — in a single binary, configured in TOML, without NGINX's operational complexity. Operators who want a built-in observability console without standing up a separate tool. Teams migrating from NGINX who want a gentler on-ramp.

Jul.IA is not the right tool if you need:

  • Fleet or multi-node management — Jul.IA runs on one node; fleet control planes are a demand-gated future milestone.
  • Kubernetes Ingress or Gateway API — no K8s controller ships today; use NGINX Ingress, Envoy Gateway, or Traefik.
  • Full service mesh or xDS — use Envoy, Istio, or Linkerd.
  • Multi-tenant edge / SaaS CDN — Jul.IA has no tenant isolation layer.
  • Envoy-level extensibility or ecosystem — Jul.IA's plugin system is WASM-based and purpose-fit, not a general xDS/filter chain.

Current direction

Jul.IA is completing the bounded standalone gateway first: current correctness, trusted client/backend identity, generic resilience, safe configuration automation, and selected high-value runtime dynamics. Fleet, hosted cloud, GraphQL composition and AI remain optional horizons or bounded experiments; none is required for the single-node product to remain useful. See the operating model, Core Gateway Completeness, and roadmap v2.0.


Feature maturity

The canonical maturity matrix lives in docs/status.md. At a glance:

Maturity Features
GA Core HTTP, TLS & ACME, Authentication, mTLS, Console, Active health checks, WAF, Rate limiting, Compression, OTel tracing, Response cache, Zero-config + jul lint, NGINX importer, HTTP/3, gRPC transcoding + passthrough, Service discovery, Secrets references, WASM plugins, L4 stream proxy
GA — soak pending (none — all shipped features are GA)

All shipped features meet Jul.IA's nine-criteria GA bar (conformance matrix, benchmarks, docs, examples, security threat note, fuzzing where applicable, stable config/API contract, and Console surface), including the post-GA soak gate per ADR 0005. See docs/status.md for the full matrix and per-feature soak-evidence links. A soak failure on a GA feature is a release-blocking regression, not a reason to retract the label.

Delivery state ≠ maturity. "GA" describes the stable released maturity contract. The immutable v1.32.1-rc.1 tag is an independently verified published prerelease candidate, not a stable release: it contains all 12 platform/architecture/profile archives, verified checksums and embedded SPDX SBOMs, plus SLSA provenance and SPDX SBOM attestations for every binary. It covers strict unknown- and known-value validation, HTTP/3 mTLS parity, compression no-transform, exclusive ACME challenge selection, the frozen Prometheus contract, bounded/redacted WAF request logging, explicit access-log enablement, and dependency/CI fixes. See the candidate evidence. Current main has since completed the response-cache correctness programme: the cache retains GA on the strength of the 2026-08-07 recertification — source audit, executable behavior matrix, race and protocol evidence, benchmarks and soak. See docs/cache.md and docs/status.md; the combined audit is retained as the dated record that programme closed out.

Many features require an opt-in build tag (e.g. grpc, acme, wasmplugins, stream, http3, waf, consul, kubernetes). The default lean binary ships the core GA surface plus core compression (gzip). Build with -tags "…" or download the full release profile to enable everything.


Features

Area Capability
Static files Document root serving, index files, try_files, optional directory listing, hidden-file control, Cache-Control headers
Reverse proxy proxy_pass to a concrete URL or a named upstream; per-location connect/read/send timeouts; custom upstream headers with variable expansion
WebSocket & SSE Transparent passthrough of Connection: Upgrade (HTTP 101) connections — text and binary frames spliced bidirectionally (Apollo GraphQL subscriptions, Socket.IO) — and text/event-stream / chunked responses streamed per write, never buffered (Node/Python SSE)
Load balancing round_robin, weighted_round_robin, and least_conn strategies across an upstream pool
Health & failover Passive health checking (max_fails / fail_timeout) plus optional active HTTP/TCP probes ([upstreams.health_check]), with automatic retry of idempotent requests against healthy backends
Service discovery Resolve an upstream's backends dynamically and refresh the pool live without a reload ([upstreams.discovery]): DNS A/AAAA and DNS SRV in every build, plus Consul and Kubernetes EndpointSlices behind the consul/kubernetes build tags — failed or empty resolves keep the last-good backends
App gateways fastcgi_pass (e.g. PHP-FPM) and uwsgi_pass (Python/WSGI) with full CGI parameter mapping
gRPC transcoding Expose a gRPC service as a RESTful JSON API via google.api.http annotations (grpc_transcode) — unary and streaming (server/client/bidi, NDJSON or SSE) — from a compiled descriptor set or server reflection, opt-in grpc build tag
gRPC passthrough Reverse-proxy native gRPC end to end over HTTP/2 (grpc = true) — trailers preserved, streaming frames flushed immediately, load balancing and health checks applied — with cleartext h2c inbound (h2c = true) for clients without TLS, opt-in grpc build tag
Response cache Two-tier (in-memory + optional disk overflow) cache with TTL, stale-while-revalidate, and admin purge; currently under correctness recertification — see docs/cache.md
Compression On-the-fly gzip (every build) plus br/zstd codings (via the brotli/zstd build tags); Accept-Encoding negotiation, Cache-Control: no-transform, MIME allow-list, size threshold, and precompressed .br/.gz sidecars
Rate limiting Token-bucket request limiting keyed by client IP, a request header, or a JWT claim, with burst, global or per-location policy, and 429 + Retry-After; plus a per-listener concurrent-connection cap
Access control Per-location CIDR allow/deny lists plus one credential method — HTTP Basic (bcrypt htpasswd), JWT bearer tokens validated against a JWKS endpoint (asymmetric algorithms only, none rejected), or forward-auth to an external service
WAF ModSecurity-compatible web application firewall (Coraza) with the OWASP Core Rule Set embedded in the binary ([waf], global or per-location): block/detect modes, paranoia levels, your own SecLang files or inline rules, request/response body inspection, and a jul_waf_events_total metric — opt-in waf build tag (docs/waf.md)
Secrets references Keep credentials out of the config file: any string field accepts ${env:NAME}, ${file:/path}, or ${secret:/path} references resolved at serve time, resolved values are masked from logs, and jul lint flags literal admin/Consul/Kubernetes tokens — core, no build tag (docs/secrets.md)
Egress allow-list Optional hardening ([egress]) that constrains the server's own config-driven fetches — JWKS, forward-auth, Consul/Kubernetes discovery, ACME/OCSP, and the WASM plugin fetch intersection — to an approved set of hosts/CIDRs, refused at connect time; bounds the SSRF blast radius of a misconfigured or compromised config, disabled by default — core, no build tag (docs/egress.md)
TLS TLS 1.2/1.3 termination per server block, configurable minimum version, optional HTTP→HTTPS redirect
Automatic HTTPS ACME certificate issuance and auto-renewal using the configured exclusive HTTP-01 or TLS-ALPN-01 challenge, with on-disk account/certificate cache — opt-in acme build tag
HTTP/3 HTTP/3 over QUIC on the same address (UDP), sharing the complete server TLS/mTLS policy and certificate provider, advertised through Alt-Svc; static certificate-file changes remain restart-bound — opt-in http3 build tag
Routing exact, prefix, and regex location matching; regex rewrites with last/break/redirect/permanent flags
Virtual hosts Multiple server_names per listener; multiple listen addresses
Limits & timeouts client_max_body_size, header size caps, read/write/idle/header timeouts (per-server, location overrides for body size)
Redirects return, redirect, and deny (403) location actions; custom error pages
Hot reload Zero-downtime config reload via SIGHUP, file-watch, or the admin API — invalid configs are rejected and the old config keeps serving
Observability Structured logging (text/JSON), pluggable access-log sinks (file/syslog with rotation), Prometheus metrics, OpenTelemetry tracing, health/readiness probes
Admin GUI Loopback-bound web console with live metrics, upstream/certificate status, config history and rollback, setup and structured editors. Legacy shared-token mode remains available; opt-in local multi-principal RBAC with predefined/custom roles, scoped revocable tokens and per-principal audit attribution is shipped (console build tag). External OIDC/SAML/SCIM identity is not shipped.
Developer experience Zero-config jul run --serve/--proxy (no file needed), jul lint best-practice checks with CI-friendly exit codes, and jul fmt canonical formatting
Migration jul import nginx translates an existing NGINX config to Jul.IA TOML, reporting every directive it could not map — opt-in importer build tag
WebAssembly plugins Sandboxed request middleware and handlers compiled to WASM and run on the embedded wazero runtime (pure Go, no cgo): per-plugin memory and time limits, panic isolation, capability-gated key/value store, hot-reloadable — opt-in wasmplugins build tag
L4 stream proxy TCP and UDP reverse proxying ([[stream]]) with load balancing and health checks across an upstream pool, TLS SNI routing by host without terminating, and HAProxy PROXY protocol v1/v2 (in and out) to preserve the client address — survives hot reload, opt-in stream build tag
Portability Single static binary, no runtime dependencies; Windows, Linux, and macOS on amd64/arm64

Note: The [[stream]] (L4 proxy) table is active in binaries built with the stream tag; in a binary without that tag a populated [[stream]] table is rejected at startup. The [plugins] table is active in binaries built with the wasmplugins tag; in a binary without that tag a populated [plugins] table is rejected at startup. The [waf] table (and per-location waf override) is active in binaries built with the waf tag; in a binary without that tag an enabled WAF config is rejected at startup.


Performance & benchmarks

Jul.IA ships with an in-tree benchmark suite covering the hot path (routing, TLS, auth, proxy, cache, gRPC, and HTTP/3). For how to run benchmarks, what each measures, and tuning recommendations (connection pooling, cache sizing, compression levels, worker limits), see docs/benchmarks.md.


Installation

Download the archive for your platform from a release (or build it yourself — see Building from source) and extract it. Each archive contains the binary, a sample server.toml, the SBOM, and README/SECURITY docs.

Archives are named jul_<version>_<os>_<arch>_<profile>.(tar.gz|zip) and ship in two profiles: lean (the default build, no optional features) and full (every opt-in feature — Brotli/Zstd, ACME, console, OTel, gRPC, HTTP/3, importer, WASM plugins, stream proxy, Consul/Kubernetes discovery, WAF). Pick full unless you specifically want the smaller lean binary.

Platform Archive (full profile)
Windows (Intel/AMD 64-bit) jul_<version>_windows_amd64_full.zip
Windows (ARM64) jul_<version>_windows_arm64_full.zip
Linux (Intel/AMD 64-bit) jul_<version>_linux_amd64_full.tar.gz
Linux (ARM64) jul_<version>_linux_arm64_full.tar.gz
macOS (Apple Silicon) jul_<version>_darwin_arm64_full.tar.gz
macOS (Intel) jul_<version>_darwin_amd64_full.tar.gz

Swap full for lean for the minimal build. Verify your download and the build provenance before running it — see docs/release.md for the sha256 checksums, SBOM, and gh attestation verify steps, plus the full list of variants and per-platform install notes.

Not sure which architecture you need?

  • Windows: echo $env:PROCESSOR_ARCHITECTUREAMD64 or ARM64
  • Linux/macOS: uname -mx86_64 (amd64) or aarch64/arm64

Quick start

From the extracted folder (or the repo root if running from source):

Windows (PowerShell):

.\jul.exe --config .\server.toml

Linux / macOS:

chmod +x ./jul
./jul --config ./server.toml

From source (Go installed):

go run ./cmd/jul --config server.toml

Validate a configuration without starting the server:

jul check -config server.toml

Print the version:

./jul --version

Command-line usage

jul [flags]                                   run the server (default)
jul serve [-config f]                         run the server (explicit form)
jul check [-config f] [-json] [-quiet]        full runtime preflight check
jul healthcheck [-config f] [-addr h:p | -url u] [-ready] [-timeout d] [-json] [-quiet]
                                              probe a running server's health endpoint
jul lint [-config f] [-strict] [-json] [-quiet]
                                              validate + best-practice checks
jul fmt  [-config f] [-w] [-diff]             rewrite the config in canonical TOML
jul run  --serve <dir> | --proxy <target> [--listen addr]
                                              run a zero-config server (no file)
jul import nginx [-o out.toml] [-strict] <nginx.conf>
                                              translate an NGINX config (importer tag)
jul version [-json]                           print version and build metadata
jul completion <bash|zsh|fish|powershell>     print a shell completion script

Legacy flags (default command, still supported; deprecated — prefer the subcommands above):
  --config string   path to the TOML configuration file (default "server.toml")
  --check           validate the configuration and exit  (prefer "jul check")
  --version         print version and exit               (prefer "jul version")

jul check

Performs a full runtime preflight: it validates structurally and dry-runs every component that could fail during serve/reload (WAF rule compilation, auth initialisation, compression encoder availability, plugin compile, etc.). This is stronger than jul lint, which only checks schema and best-practice warnings. Use check in CI before deploying, or locally when you want certainty that the binary you built can actually start with this config.

jul check -config server.toml
jul check -config server.toml -json   # machine-readable output

Exit codes: 0 ok, 1 validation or runtime error. The legacy --check flag on the default command (jul -check) is equivalent but jul check is the canonical subcommand.

jul healthcheck

Probes a running server's admin health endpoint and exits with a deterministic status, so it can drive container, systemd, and Kubernetes liveness/readiness checks — including from a shell-less distroless image where curl/wget are unavailable. It reads the [admin] listen address from the config (or takes -addr/-url) and GETs /healthz (liveness) or, with -ready, /readyz (readiness). The admin listener must be enabled.

jul healthcheck                                       # discover [admin] listen from server.toml
jul healthcheck -config /etc/jul/server.toml -ready   # readiness probe
jul healthcheck -addr 127.0.0.1:9090 -quiet           # exit code only, no output
jul healthcheck -url http://127.0.0.1:9090/healthz -json

Exit codes: 0 healthy (endpoint returned 2xx), 1 unhealthy (non-2xx, or the server was unreachable / timed out), 2 usage or config error (bad flags, unreadable config, or the admin listener is disabled). The health verdict is strictly 0/1, so the command is safe to use directly in a Docker HEALTHCHECK — see deployment.md.

jul lint

Parses and validates the configuration and additionally reports best-practice warnings in a single pass: an unauthenticated off-loopback admin listener, disabled compression, TLS without an explicit min_version, unreachable (duplicate) locations, directory listing exposure, and servers without locations. Each finding includes a hint. Exit codes are CI-friendly: 0 when there are no errors, 1 on validation errors, and 2 when warnings are present under -strict. Parse errors point at the offending line and column.

jul fmt

Validates and rewrites the configuration into canonical TOML. Invalid known values are rejected before anything is printed or written. By default it prints to stdout; -w writes the result back to the file. -diff shows a unified diff of the changes without writing: exits 0 when nothing would change, 1 when changes are needed (useful for CI enforcement). Comments and original formatting are not preserved.

jul run (zero-config)

Starts a server from a synthesized profile without any config file:

jul run --serve ./public            # serve a directory of static files
jul run --proxy 127.0.0.1:3000      # reverse-proxy everything to a backend
jul run --proxy :3000 --listen :80  # proxy to loopback :3000, listen on :80

Zero-config defaults enable compression and sensible timeouts. The default listen address is :8080.

jul import

Translates an existing NGINX configuration into Jul.IA TOML. It is gated behind the importer build tag (see Migrating from NGINX):

jul import nginx /etc/nginx/nginx.conf            # write TOML to stdout
jul import nginx -o server.toml /etc/nginx/nginx.conf

Every directive it cannot translate is reported with its source line, both on stderr and as a comment header in the output, so nothing is dropped silently. The generated config is re-parsed and validated before it is emitted. Exit codes: 0 ok, 1 parse/translate error or invalid output, 2 warnings under -strict.

jul version

Prints the version and build metadata. Human-readable by default; -json emits a stable machine object (keys: product, version, commit, build_date, dirty, go_version, os, arch) for scripts and CI.

jul version           # human-readable
jul version -json     # machine-readable object

The version string is stamped by the release pipeline (and make build); the commit, build date, and dirty flag are read from the Go build info the toolchain embeds automatically, so they populate for any go build from the repository and degrade to unknown when VCS metadata is absent. To stamp a custom version:

go build -ldflags "-X main.version=1.2.3" -o jul ./cmd/jul

jul completion

Generates a shell completion script for bash, zsh, fish, or powershell (pwsh). Source it for the current session, or install it into the shell's completion directory:

source <(jul completion bash)                                # bash, current session
jul completion zsh  > "${fpath[1]}/_jul"                     # zsh, installed
jul completion fish > ~/.config/fish/completions/jul.fish    # fish
jul completion powershell | Out-String | Invoke-Expression   # PowerShell

Completion covers the subcommand verbs and the arguments of completion and version; file paths complete elsewhere.

CLI troubleshooting

For a full incident playbook — startup, reloads and restart-required changes, service discovery, plugins, and soak interpretation — see docs/troubleshooting.md.

  • Config rejected on start or reload? Run jul lint -config server.toml to see every error and warning at once; the running server keeps its last valid configuration on a failed reload.
  • Not sure a change is safe? jul lint before reloading, or rely on the admin GUI which validates before applying.
  • Want a quick local server? jul run --serve . needs no config file.

Migrating from NGINX

jul import nginx reads an NGINX configuration file and produces an equivalent Jul.IA TOML config. It is a best-effort migration aid, not a 1:1 converter: common directives are translated, and everything it cannot map is reported with its source line so you can port it by hand. The importer is gated behind the importer build tag to keep the default binary lean:

go build -tags importer -o jul ./cmd/jul
jul import nginx -o server.toml /etc/nginx/nginx.conf

The generated file is re-parsed and validated exactly as the server would load it, so a successful run always yields a config that passes jul lint. Run the import, then review the # TODO comments at the top of the output and the summary printed to stderr.

What gets translated

NGINX Jul.IA
http { ... } top-level config
server { ... } [[servers]]
listen 80; / listen 443 ssl; listen = ":80" / ":443" + [servers.tls]
server_name a b; (_ dropped) server_names = ["a", "b"]
root / index / try_files location root / index / try_files
location / { ... } [[servers.locations]] with a prefix match
location = /p / ^~ /p / ~ re / ~* re exact / prefix / regex / regex match
proxy_pass http://name; location proxy_pass (a bare host gets http://)
fastcgi_pass location fastcgi_pass
return 301 https://h/; / return 404; location redirect + return / return
rewrite re repl flag; location [[...rewrites]]
upstream name { server ... } [[upstreams]] with servers
server h weight=3; upstream server weight (→ weighted_round_robin)
least_conn; upstream strategy = "least_conn"
ssl_certificate / ssl_certificate_key [servers.tls] cert / key
ssl_protocols TLSv1.2 TLSv1.3; [servers.tls] min_version
gzip on; [compression] enabled = true

What is not translated (reported, not dropped)

  • include directives are not followed — import each included file separately, or concatenate them first.
  • Process-level directives (worker_processes, events, pid, user, …) have no per-server equivalent and are ignored.
  • stream / mail modules, map / geo / if blocks, named locations (@name), Lua, and any directive without a Jul.IA equivalent (add_header, proxy_set_header, client_max_body_size, autoindex, …) are listed in the report for manual porting.

See examples/migrate for a sample nginx.conf, the config it produces, and a walkthrough.


Configuration reference

Jul.IA is configured by a single TOML document. The top-level tables are [global], [[servers]], [[upstreams]], [cache], and [admin].

A minimal, working example:

[global]
log_level = "info"
shutdown_timeout = "30s"

[[servers]]
listen = "0.0.0.0:8080"
server_names = ["localhost", "example.com"]

  [[servers.locations]]
  match = { type = "prefix", path = "/" }
  root  = "/srv/www/example"
  index = ["index.html", "index.htm"]
  try_files = ["$uri", "$uri/", "/index.html"]

  [[servers.locations]]
  match = { type = "prefix", path = "/api/" }
  proxy_pass = "http://backend"
  cache = true

    [servers.locations.headers]
    Host = "$host"
    X-Real-IP = "$remote_addr"
    X-Forwarded-For = "$proxy_add_x_forwarded_for"

[[upstreams]]
name = "backend"
strategy = "round_robin"
servers = ["127.0.0.1:3000", "127.0.0.1:3001"]

[cache]
enabled = true
memory_max_size = "64m"
default_ttl = "60s"
stale_while_revalidate = "30s"
stale_if_error = "300s"

[admin]
enabled = true
listen = "127.0.0.1:9090"
token = "change-me"
console = true

The full configuration reference — every key, type, default, and example — lives in docs/configuration.md so it can be updated independently and deep-linked.

Key sections covered there:

About

Jul.IA - Go edge/web server: reverse proxy, static serving, FastCGI/uWSGI, two-tier cache, hot reload, admin console; opt-in ACME, compression, OpenTelemetry, gRPC transcoding, HTTP/3.

Topics

Resources

Contributing

Security policy

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages