Skip to content
oarkflowPublic

About

No description, website, or topics provided.

Resources

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

115 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

fh — Zero-Dependency Go Web Framework

fh is a standalone, high-performance HTTP/1.1 + HTTP/2 + WebSocket web framework for Go with no third-party dependencies beyond golang.org/x/crypto (used only for optional OCSP stapling and ACME certificate automation). It implements HTTP parsing, routing, HTTP/2 framing, HPACK, and WebSocket protocols from scratch — no wrappers around net/http or fasthttp.

Full reference documentation lives in docs/.

Features

  • Minimal dependencies — the Go standard library plus golang.org/x/crypto, both optional (OCSP stapling, ACME)
  • HTTP/1.1 — full request/response parsing, chunked transfer, trailers
  • HTTP/2 — TLS ALPN, optional h2c prior knowledge/upgrade, stream multiplexing, flow control, RFC 8441 extended CONNECT
  • WebSocket — RFC 6455 server implementation with EventHub pub/sub layer, transparently served over HTTP/1.1 or HTTP/2
  • Trie-based router — radix tree with named (:param) and wildcard (*wild) parameters
  • Codec system — pluggable body parsers for JSON, XML, form, multipart, CSV, NDJSON, text, binary
  • 70+ built-in middleware packages — see Middleware below
  • Typed endpoints & OpenAPI 3.1 — generic request/response handlers with auto-generated specs
  • Reliability layer — request journaling, idempotency, durable async queue, outbox/inbox, DLQ
  • Compliance layer — Business/Professional/Enterprise/Security profiles, audit ledger, route security metadata
  • Opt-in fail-closed baseline — fh.WithSecureByDefault(true) bounds every protocol input, enables strict parsing, recovery, redaction, and hardened response headers. Neither this nor fh.NewProduction() adds authentication, CSRF protection, rate limiting, or a Host allow-list — see Production Readiness
  • Template engine — agnostic interface, any engine implementing Render(w, name, data, layout...)
  • Static file serving — direct streaming, range requests, streaming gzip, precompressed Brotli/gzip, cache control
  • Streaming uploads — opt-in incremental HTTP/1 body consumption with bounded draining and trailer support
  • Graceful shutdown — app.ServeContext(ctx, listener), app.ListenContext(ctx, addr), app.ListenUnixContext(ctx, path), app.ShutdownWithContext(ctx), or app.ListenWithGracefulShutdown(addr)
  • Graceful TLS shutdown — app.ListenTLSWithGracefulShutdown(addr, certFile, keyFile)
  • Pool-based zero-allocation — sync.Pool for contexts, byte buffers, HPACK decoders
  • Hardened TLS/mTLS — TLS 1.3 config builder, verified peer state in request contexts, atomic certificate reload
  • ACME / Let's Encrypt — app.ListenAutoTLS(domains, cacheDir) issues and renews certificates automatically via TLS-ALPN-01
  • Prefork & zero-downtime restarts — app.ListenPrefork(addr) runs a multi-process SO_REUSEPORT supervisor; SIGHUP rolls out a new binary with zero dropped connections
  • Outbound HTTP client — connection pooling, retries, circuit breaker, SSRF protection (fh.NewClient)
  • Linux kernel-assisted transport — raw sockets, sharded epoll or io_uring, SO_REUSEPORT CPU steering, socket tuning, and optional XDP admission with safe fallback

Installation

go get github.com/oarkflow/fh

Requires Go 1.26.5 or later.

Quick Start

package main

import (
    "log"

    "github.com/oarkflow/fh"
)

func main() {
    app := fh.New()

    app.Get("/", func(c fh.Ctx) error {
        return c.SendString("Hello, World!")
    })

    log.Fatal(app.ListenWithGracefulShutdown(":8080"))
}

Production application template

Generate a structured server with secure sessions, strict Host/origin policy, RBAC, bounded requests, rate limiting, audit logs, protected health/metrics, SQLite persistence, graceful shutdown, encrypted WASM Fetch with signed ciphertext responses, and a structured, componentized browser frontend ("FH Control Center", built with @oarkflow/lithe):

go run ./cmd/fh-init -module example.com/acme/service -dir ./service
cd service
./run.sh

The generator initializes .env from .env.example and preserves it on subsequent -force regeneration. Add -verify to have it build the server, boot it on a spare loopback port, walk through the app's full HTTP surface (assets, the WASM transport bundle, and the login/secure-bootstrap/logout lifecycle), and open it in your browser - see the generated README's fh-init -verify section. examples/production-app is a checked-in, buildable-in-CI copy of this template's current output.

Loopback development uses short-lived process-local cryptographic keys. The generated server fails closed outside the explicit development environment unless HTTPS and persistent session, login, operations, transport, and response signing secrets are configured. See the generated README and secure WASM transport guide before deployment.

Cross-platform kernel-assisted transport

fh keeps protocol parsing, TLS, routing, middleware, reliability and handlers in memory-safe Go while using the native kernel network facility on each supported server OS:

  • Linux: raw nonblocking sockets with sharded epoll; opt-in probed io_uring; optional SO_REUSEPORT BPF steering and XDP admission.
  • macOS and BSD: raw nonblocking sockets with sharded kqueue accept reactors.
  • Windows: IOCP/overlapped networking through Go's native network poller.
  • Solaris/illumos: event ports through Go's native network poller.
  • AIX: pollset through Go's native network poller.
  • Other server-capable targets: functional native listener backend.

The balanced production profile does not automatically select the newer custom io_uring path. Use the throughput profile or explicitly request io_uring after benchmarking and canary testing it on the deployment kernel.

kernel := fh.ProductionKernelConfig()
kernel.Required = true

app := fh.NewProduction(fh.WithKernel(kernel))
if err := app.ValidateKernelProduction(); err != nil {
    log.Fatal(err)
}
app.Get("/", func(c fh.Ctx) error { return c.SendString("kernel-assisted") })
log.Fatal(app.ListenWithGracefulShutdown(":8080"))

For an aggressive throughput candidate:

kernel := fh.HighPerformanceKernelConfig()
// On Linux this permits probed io_uring auto-selection. Benchmark it against
// ProductionKernelConfig before deployment.

Inspect app.KernelRuntimeInfo() and app.KernelReadiness() at runtime. They report the backend that actually started, fallbacks, connection admission, socket-option failures and deployment warnings. Readiness always requires a workload benchmark because no static configuration is universally fastest.

Probe Linux capabilities without changing the host:

go run ./cmd/fh-kernelctl probe

Optional XDP support is Linux-only and never attached unless explicitly enabled. See kernel-assisted transport and the complete example.

Routing

app.Get("/path", handler)
app.Post("/path", handler)
app.Put("/path", handler)
app.Delete("/path", handler)
app.Patch("/path", handler)
app.Head("/path", handler)
app.Options("/path", handler)
app.All("/path", handler)          // register all methods
app.Add("GET", "/path", handler)   // explicit method string

// Route parameters
app.Get("/users/:id", func(c fh.Ctx) error {
    return c.SendString("User: " + c.Params("id"))
})
app.Get("/files/*path", func(c fh.Ctx) error {
    return c.SendString("File: " + c.Params("path"))
})

// Named routes
app.Get("/users/:id", handler).Name("user.show")
c.RedirectTo("user.show", fh.Map{"id": "42"})

// Route groups
api := app.Group("/api")
api.Get("/users", listUsers)
admin := api.Group("/admin", adminMiddleware)
admin.Get("/dashboard", dashboardHandler)

Typed endpoints (GetTyped, PostTyped, ... AllTyped) provide automatic JSON parsing, validation, struct binding (param/query/header/cookie tags), and OpenAPI schema generation. See Native Features.

See Routing for the full reference.

Middleware

app.Use(logger.New(), recover.New(), cors.New(cors.Config{
    AllowOrigins: []string{"https://example.com"},
}))

// Route- or group-level
app.Get("/dashboard", authMiddleware, dashboardHandler)
admin := app.Group("/admin", authMiddleware, adminLogger)

Commonly used packages:

Package Description
mw/basicauth HTTP Basic Authentication (single-user, multi-user, storage-backed)
mw/apikey API key authentication via header or query
mw/cors Cross-Origin Resource Sharing
mw/csrf CSRF protection
mw/ratelimiter Rate limiting
mw/cache Response caching with TTL
mw/compress / mw/decompress Gzip response compression / bounded request decompression
mw/security Security headers (CSP, HSTS, XFO, etc.)
mw/session Cookie-based sessions with HMAC signing
mw/logger Request logging (common, combined, tiny, json, custom)
mw/recover Panic recovery
mw/requestid / mw/correlationid Request tracking and correlation
mw/realip Trusted proxy-chain parsing, client-IP normalization
mw/timeout / mw/bodylimit Request timeout and body-size limits
mw/circuitbreaker / mw/bulkhead / mw/loadshed Overload and fault protection
mw/proxy Reverse proxy and API gateway handlers
mw/mtls Verified client-certificate authorization
mw/httpsignature Nonce-bound RFC 9421 Ed25519 response signatures
mw/metrics Prometheus-style metrics endpoint

This is a subset — fh ships 70+ middleware packages under mw/, each with its own README.md. See docs/middleware.md for the full reference and recommended ordering, or mw/README.md for the package index.

Body Parsing & Codecs

BodyParser automatically selects the right codec based on Content-Type:

var user User
if err := c.BodyParser(&user); err != nil {
    return err
}
Content-Type Codec
application/json JSON
application/xml, text/xml XML
application/x-www-form-urlencoded Form
multipart/form-data Multipart
text/csv CSV
application/x-ndjson NDJSON
text/plain Plain text
application/octet-stream Binary

Register custom codecs with fh.RegisterCodec(&MyCodec{}). See Codecs.

Responses

c.SendString("text")
c.SendBytes([]byte("data"))
c.SendStream(reader)
c.JSON(data)
c.XML(data)
c.HTML(html)
c.SendFile("path/to/file.pdf")
c.Redirect("/new-path")
c.RedirectTo("route.name", fh.Map{"id": "42"})
c.Status(201).JSON(createdResource)

See Request & Response for the full method reference.

Static Files

import "os"

app.Static("/static", "./public")

app.StaticFS("/", os.DirFS("./public"), fh.StaticConfig{
    Compress:     true,
    Browse:       true,
    IndexFiles:   []string{"index.html", "index.htm"},
    CacheControl: "public, max-age=3600",
})

HTTP/2

fh supports TLS + ALPN (app.ListenTLS(":443", "cert.pem", "key.pem")), h2c prior knowledge, and h2c upgrade from HTTP/1.1. Use fh.WithDisableH2C(true) on cleartext listeners that should accept only HTTP/1; WithSecureByDefault(true) applies that restriction automatically. fh also implements RFC 8441 extended CONNECT, so WebSocket (and other c.Upgrade-based protocols) tunnel over a single HTTP/2 stream instead of requiring an HTTP/1.1 fallback. See HTTP/2.

WebSocket

import "github.com/oarkflow/fh/pkg/websocket"

app.Get("/ws", websocket.New(func(conn *websocket.Conn) error {
    opcode, payload, err := conn.ReadMessage()
    if err != nil {
        return err
    }
    return conn.WriteMessage(opcode, payload)
}))

For pub/sub with rooms, topics, auth, and heartbeats, use pkg/websocket.EventHub:

import "github.com/oarkflow/fh/pkg/websocket"

hub := websocket.NewEventHub(websocket.EventHubConfig{
    Auth: func(client *websocket.EventConn, env websocket.Envelope) error {
        // Revalidate authorization for every non-ack event.
        return nil
    },
})
defer hub.Close()

hub.On("chat.message", func(ctx *websocket.HandlerContext) (any, error) {
    return map[string]any{"accepted": true}, nil
})

wsConfig := websocket.DefaultConfig()
wsConfig.AllowedOrigins = []string{"https://app.example.com"}
app.Get("/ws", hub.Handler(wsConfig, nil))

_ = hub.BroadcastEvent("chat", "general", "chat.message", "Hello everyone!")

See WebSocket.

Error Handling

fh includes a production-safe error framework based on RFC 9457 Problem Details, with typed errors, validation errors, panic recovery, request ID correlation, retryability metadata, and secret redaction.

return fh.NotFound("User not found")
return fh.Unauthorized("Sign in required")
return fh.NewHTTPError(fh.StatusConflict, "USER_EXISTS", "User already exists")

app := fh.NewWithConfig(fh.Config{
    ErrorHandler: func(c fh.Ctx, err error) { _ = c.ErrorResponse(err) },
    NotFoundHandler: func(c fh.Ctx) error {
        return c.Status(fh.StatusNotFound).JSON(fh.Map{"error": "missing"})
    },
})

See Error Framework.

Configuration

app := fh.NewWithConfig(fh.Config{
    ReadTimeout:          10 * time.Second,
    WriteTimeout:         10 * time.Second,
    IdleTimeout:          120 * time.Second,
    RequestBodyTimeout:   10 * time.Second,
    TLSHandshakeTimeout:  10 * time.Second,
    HTTP2IdleTimeout:     60 * time.Second,
    MaxConnections:       10_000,
    MaxRequestBodySize:   4 * 1024 * 1024, // 4MB
    MaxConcurrentStreams: 128,
    ErrorHandler:         customErrorHandler,
    TemplateEngine:       myEngine,
    Debug:                false,
})

See Configuration for the full field reference, and Startup Banner for the ASCII banner shown on Listen.

Sessions

import (
    "time"

    "github.com/oarkflow/fh/mw/session"
    "github.com/oarkflow/fh/pkg/storage/kv"
)

manager := session.NewSessionManager(
    kv.NewMemoryStore(kv.WithGCInterval(time.Hour), kv.WithMaxEntries(100000)),
    session.SessionSecret([]byte("at-least-32-bytes-of-random-secret")),
)
app.Use(session.New(manager))

app.Get("/login", func(c fh.Ctx) error {
    sess := session.Get(c)
    sess.Set("user_id", 42) // the middleware saves the session automatically
    return c.SendString("logged in")
})

Graceful Shutdown

// One-liner with SIGINT/SIGTERM handling
app.ListenWithGracefulShutdown(":8080")

// Manual
go app.Listen(":8080")
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
<-quit
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
app.ShutdownWithContext(ctx)

// Context-owned serving for embedded servers and process managers.
// Canceling ctx drains active connections and closes the listener.
ln, _ := net.Listen("tcp", ":8080")
serveCtx, cancel := context.WithCancel(context.Background())
defer cancel()
app.ServeContext(serveCtx, ln)

Prefork & Zero-Downtime Restarts

// Multi-process SO_REUSEPORT supervisor instead of a single process.
// The binary re-executes itself once per worker, so main() (including route
// registration) naturally runs again in every worker.
app.ListenPrefork(":8080")

Send SIGHUP to the master process to roll out a new binary with zero dropped connections: it spawns a fresh generation of workers, waits for them to report a bound listener, then gracefully drains and terminates the previous generation. SIGINT/SIGTERM stops the whole supervisor. On Windows (no SIGHUP), call app.Reload() instead. See Prefork.

ACME / Automatic TLS

// Certificates issued and renewed automatically via TLS-ALPN-01
// (golang.org/x/crypto/acme/autocert — already a dependency, no net/http
// required). CacheDir persists them across restarts.
app.ListenAutoTLS([]string{"example.com"}, "/var/lib/fh/acme-cache")

See ACME.

Reliability Layer

An optional, stdlib-only runtime for request journaling, idempotency, and a durable async job queue — no external queue dependency required.

app := fh.NewWithConfig(fh.Config{
    Reliability: fh.ReliabilityConfig{
        Enabled:            true,
        DataDir:            ".fh-data",
        JournalEnabled:     true,
        IdempotencyEnabled: true,
        QueueEnabled:       true,
        QueueWorkers:       2,
        QueueMaxAttempts:   5,
    },
})
curl -i -X POST http://localhost:3000/orders \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: order-create-001' \
  -d '{"item":"book"}'

Storage is pluggable (pkg/storage/memory, pkg/storage/sql for PostgreSQL/MySQL/SQLite, or implement the interfaces yourself). See Reliability Layer.

Compliance Layer

A built-in Business/Professional/Enterprise/Security compliance layer on top of the router, middleware, reliability, OpenAPI, queue, and security primitives.

app := fh.NewWithConfig(fh.Config{
    Mode: fh.ModeProduction,
    Compliance: fh.ComplianceConfig{
        Enabled:         true,
        Profile:         fh.ComplianceEnterprise,
        Strict:          true,
        ExposeEndpoints: true,
    },
    Audit: fh.AuditConfig{Enabled: true, FilePath: ".fh-data/audit.jsonl", Redact: true},
})

Profiles: business, professional, enterprise, security_strict, financial, healthcare, government, internal_service, public_api, webhook_receiver.

Annotate routes with security metadata so tooling can prove which controls apply:

app.Post("/payments", fh.RequireAuth(), fh.RequireScope("payments:create"), createPayment).
    WithRouteSecurity(fh.RouteSecurityConfig{
        AuthRequired:        true,
        Scopes:              []string{"payments:create"},
        IdempotencyRequired: true,
        AuditRequired:       true,
        DataClass:           "regulated",
    })

When Compliance.ExposeEndpoints is enabled, fh registers /_fh/compliance, /_fh/compliance/controls, /_fh/compliance/findings, /_fh/config/safe, /_fh/runtime, /_fh/routes, /_fh/health, /_fh/live, /_fh/ready, and (when the queue is enabled) /_fh/queue/stats and admin queue ops endpoints.

Outbound HTTP Client

A production-grade HTTP/1.1 + HTTP/2 client lives directly in the root fh package — no separate client/ module.

client := fh.NewClient(fh.ClientConfig{})
resp, err := client.Get(ctx, "https://api.example.com/users")

user, err := fh.GetJSON[User](ctx, client, "https://api.example.com/users/1")

Includes fluent request building, typed helpers (GetJSON[T], PostJSON[Req,Res]), retry policies with jitter backoff, circuit breaker, bulkhead, rate limiting, outbound SSRF protection, and streaming/atomic downloads. See HTTP Client.

Secure WASM Transport

mw/securetransport, the shared pkg/securetransport protocol, and a TypeScript/JavaScript TinyGo/WASM Fetch client under wasm/ provide device-signed session establishment, X25519 key agreement, AES-256-GCM encrypted bodies/headers, replay prevention, and pluggable stores. The secure WASM example additionally negotiates RFC 9421/RFC 9530 Ed25519 signatures over ciphertext and verifies them before decryption.

make wasm

See Secure WASM Transport and examples/secure_wasm.

Signed HTTP Responses

mw/httpsignature and pkg/httpsignature implement a strict RFC 9421 Ed25519 response-signature profile. A fresh client nonce and the originating method and target URI bind each signed response to its request; RFC 9530 Content-Digest binds the exact response bytes.

See RFC 9421 Response Signatures and the runnable Go and WebCrypto clients in examples/rfc9421.

Examples

Full working examples in examples/:

Example Description
basic Minimal "Hello World"
flash-messages Session-backed one-time flash messages and redirects
http-modern Modern HTTP helpers and protocol behavior
kernel_server Kernel-assisted server configuration and readiness
prefork Multi-process prefork serving
secure_wasm Session + secure WASM client demo for encrypted API calls
rfc9421 RFC 9421 signed-response server plus fail-closed Go and WebCrypto clients
production-app Checked-in fh-init output: sessions, RBAC, SQLite, secure WASM transport, and the @oarkflow/lithe frontend

Testing & Benchmarks

go test ./...
go test -bench=. -benchmem ./...

See benchmarks/ for cross-framework comparisons against Fiber and fasthttp, and Performance for hot-path configuration.

Documentation

Full reference documentation is in docs/README.md, covering configuration, routing, codecs, middleware, the reliability layer, HTTP/2, WebSocket, native features (typed endpoints, OpenAPI, SSE), security, and performance.

Known Limitations

fh is a production-oriented, pre-v1 framework. The core HTTP/1.1, HTTP/2 and WebSocket paths have extensive unit, integration, race and fuzz coverage, but a specific deployment is production-ready only after the release gates in docs/production-readiness.md are satisfied. The following product limitations must also be planned around:

  • No HTTP/3 / QUIC. Only HTTP/1.1 and HTTP/2 are implemented. Terminate HTTP/3 at an edge proxy (e.g. a CDN) in front of fh if you need it.
  • No OpenTelemetry (OTLP) export. mw/tracing propagates/parses traceparent headers and mw/metrics exposes a hand-rolled Prometheus text endpoint, but neither ships an OTLP exporter to a collector (Grafana Tempo/Datadog/etc.). Bridge these yourself, or scrape the Prometheus endpoint and configure trace propagation compatible with your existing collector.
  • Process-local defaults. Middleware and cluster state use the shared pkg/storage/kv.Store interface. Defaults are in-process, so rate limits, caches, sessions, replay markers, and cluster leases are per instance unless you configure a shared provider. kv.Provider and fh.WithSharedState give isolated application-wide namespaces and lifecycle management; the built-in providers are memory and single-host files. For a distributed deployment, fh-contrib provides Redis and PostgreSQL kv.Provider adapters with cross-process-atomic Mutate. See Shared State.
  • No gRPC or GraphQL protocol handlers. Only MIME-type constants exist for GraphQL; there's no built-in gRPC server. Both are addressable via a reverse-proxy route (mw/proxy) to a dedicated service if needed.

Security

See SECURITY.md for the supported-version policy and how to report a vulnerability privately. Do not open a public issue for security reports.

License

fh is distributed under the MIT License.

About

No description, website, or topics provided.

Resources

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages