Quickstart
Install
Section titled “Install”The dev toolchain (buf, protoc plugins, sqlc, govulncheck — all pinned):
GORTEXA_VERSION="v0.27.0" # update only after testing a newer releaseexport GORTEXA_REF="$GORTEXA_VERSION"export GOTOOLCHAIN=auto/bin/bash -c "$(curl -fsSL "https://raw.githubusercontent.com/yshengliao/gortexa/$GORTEXA_VERSION/install.sh")"go install github.com/yshengliao/gortexa/cmd/gortexa@"$GORTEXA_VERSION"Run outside a checkout, the script clones the repo into ./gortexa (override with GORTEXA_DIR, delete afterwards if unwanted); it only writes persistent go env settings when it detects the broken dev-container values — a normal machine’s Go config is left untouched.
go install puts the binary in $(go env GOPATH)/bin, which most shells do not have on PATH by default — without it, the next step fails with command not found: gortexa:
export PATH="$(go env GOPATH)/bin:$PATH" # add to your shell profileRun gortexa doctor afterwards to check the Go toolchain and proto tools.
Create a project
Section titled “Create a project”gortexa create myapp --module github.com/me/myapp \ --repo https://github.com/yshengliao/gortexa.git --ref v0.27.0cd myapp# Do not print the old value: remove the usable secret generated by create.# The placeholder refuses startup without an environment override.perl -0pi -e 's/(jwt_secret:\s*")[^"]*(")/${1}dev-only-insecure-secret-change-me-please${2}/' etc/config.yamlgrep -Fq 'jwt_secret: "dev-only-insecure-secret-change-me-please"' etc/config.yamlcreate copies the framework layout, rewrites the module path and drops the git history. The result ships a sample resource, the full interceptor chain, config, health and the MCP bridge.
Generate your first API
Section titled “Generate your first API”gortexa gen billing/v1 InvoiceThis produces the proto contract (with validation and AI annotations), an in-memory logic stub and the server wiring, then runs buf lint → breaking → generate. After later proto edits, run gortexa regen.
Everything under gen/ is generated code: never hand-edited, produced only by make gen (committed, following the framework convention — the copied CI has a drift guard).
Run and verify
Section titled “Run and verify”The following single-shell smoke test uses a short-lived runtime secret, starts
the same server entrypoint used by gortexa run, waits for readiness, and registers
cleanup for its binary and log. The server remains for the next token flow; do not
change shells between these commands.
set -euo pipefailexport GORTEXA_AUTH__JWT_SECRET="$(openssl rand -hex 32)"go build -o ./.gortexa-prototype-server ./cmd/server./.gortexa-prototype-server >./gortexa-prototype.log 2>&1 &SERVER_PID=$!cleanup() { kill -TERM "$SERVER_PID" 2>/dev/null || true; wait "$SERVER_PID" 2>/dev/null || true; rm -f ./.gortexa-prototype-server ./gortexa-prototype.log; }trap cleanup EXIT INT TERMfor _ in $(seq 1 60); do if curl -fsS http://127.0.0.1:8080/healthz >/dev/null 2>&1; then break; fi sleep 0.5donecurl -fsS http://127.0.0.1:8080/healthztest "$(curl -sS -o /dev/null -w '%{http_code}' http://127.0.0.1:8080/v1/resources/x)" = 401curl -fsS -X POST http://127.0.0.1:8080/mcp \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'The final HTTP-gateway request returns 401 by design; it shares authentication with gRPC and MCP. This smoke test does not replace a live gRPC E2E or authorization test. If you will not continue to the token flow, run cleanup; trap - EXIT INT TERM now to stop the server and remove temporary files. For normal interactive development, run gortexa run in the same shell with the exported secret, then stop it with Ctrl-C.
Getting a dev token
Section titled “Getting a dev token”The server remains running in the same shell from the previous block. First ensure the temporary command does not already exist, then create its directory:
test ! -e cmd/devtokenmkdir -p cmd/devtokenSave the following secret-free program as cmd/devtoken/main.go. Replace the import if your module is not github.com/me/myapp:
package main
import ( "fmt" "os" "time"
"github.com/me/myapp/auth")
func main() { secret := os.Getenv("GORTEXA_AUTH__JWT_SECRET") v, err := auth.NewVerifier([]byte(secret), "gortexa") if err != nil { panic(err) } tok, err := v.Sign("dev", []string{"admin"}, time.Hour) if err != nil { panic(err) } fmt.Println(tok)}Run it, verify an authenticated request, then remove the temporary command and stop the server:
TOKEN="$(go run ./cmd/devtoken)"AUTH_STATUS="$(curl -sS -o /dev/null -w '%{http_code}' http://127.0.0.1:8080/v1/resources/x -H "Authorization: Bearer $TOKEN")"test "$AUTH_STATUS" = 200 || test "$AUTH_STATUS" = 404printf 'authenticated HTTP status=%s\n' "$AUTH_STATUS"rm cmd/devtoken/main.gormdir cmd/devtokencleanuptrap - EXIT INT TERMDo not copy, commit, or bake a real secret from etc/config.yaml into an image. In production, inject and rotate it through a secret manager, mounted file, or runtime environment.
Use as a module (no scaffold)
Section titled “Use as a module (no scaffold)”Every framework package is importable directly with go get — no scaffolded project required:
go get github.com/yshengliao/gortexa@latestA minimal app — one h2c port with gRPC health, /healthz, /readyz — needs no code generation at all:
package main
import ( "context" "log" "os/signal" "syscall"
"github.com/yshengliao/gortexa/health" "github.com/yshengliao/gortexa/kernel")
func main() { ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer stop()
app, err := kernel.New() // defaults: h2c on :8080, graceful shutdown if err != nil { log.Fatal(err) } app.Health().Register("self", func(context.Context) health.State { return health.Healthy })
if err := app.Run(ctx); err != nil { log.Fatal(err) }}From there, both the AI and the non-AI path are first-class:
- Plain services:
configfor layered config,interceptorfor the governed chain,httpcompatfor the gateway mux,apperrfor the three-transport error model, plusmq/cache/storage/client. - AI services: annotate your protos with
gortexa/ai/v1/annotations.proto(its Go bindings ship in the module atgen/gortexa/ai/v1) and wiremcp.NewBridgeto expose the annotated RPCs as MCP tools behind the same interceptor chain.
For each public component’s purpose, import path and a minimal example, see Components.
Note: a project created by gortexa create already contains the framework source under its own module path — never also go get the framework there, or both copies register gortexa/ai/v1/annotations.proto and the binary panics at init.
Working inside the framework checkout
Section titled “Working inside the framework checkout”When developing the framework itself, use make:
make bootstrap # install the pinned toolchainmake gen # buf lint → breaking → generatemake test # go test -racemake run # start the sample server (injects a local dev JWT secret)make dev # docker compose with Jaeger, Grafana, PrometheusNext steps
Section titled “Next steps”- Core concepts: how single-port multiplexing, the interceptor chain and the error model work.
- Configuration: every config.yaml field and the environment-variable overrides.
- Examples: 11 runnable verification examples.