Skip to content

Quickstart

The dev toolchain (buf, protoc plugins, sqlc, govulncheck — all pinned):

Terminal window
GORTEXA_VERSION="v0.27.0" # update only after testing a newer release
export 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:

Terminal window
export PATH="$(go env GOPATH)/bin:$PATH" # add to your shell profile

Run gortexa doctor afterwards to check the Go toolchain and proto tools.

Terminal window
gortexa create myapp --module github.com/me/myapp \
--repo https://github.com/yshengliao/gortexa.git --ref v0.27.0
cd 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.yaml
grep -Fq 'jwt_secret: "dev-only-insecure-secret-change-me-please"' etc/config.yaml

create 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.

Terminal window
gortexa gen billing/v1 Invoice

This 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).

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.

Terminal window
set -euo pipefail
export 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 TERM
for _ in $(seq 1 60); do
if curl -fsS http://127.0.0.1:8080/healthz >/dev/null 2>&1; then break; fi
sleep 0.5
done
curl -fsS http://127.0.0.1:8080/healthz
test "$(curl -sS -o /dev/null -w '%{http_code}' http://127.0.0.1:8080/v1/resources/x)" = 401
curl -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.

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:

Terminal window
test ! -e cmd/devtoken
mkdir -p cmd/devtoken

Save 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:

Terminal window
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" = 404
printf 'authenticated HTTP status=%s\n' "$AUTH_STATUS"
rm cmd/devtoken/main.go
rmdir cmd/devtoken
cleanup
trap - EXIT INT TERM

Do 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.

Every framework package is importable directly with go get — no scaffolded project required:

Terminal window
go get github.com/yshengliao/gortexa@latest

A 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: config for layered config, interceptor for the governed chain, httpcompat for the gateway mux, apperr for the three-transport error model, plus mq / cache / storage / client.
  • AI services: annotate your protos with gortexa/ai/v1/annotations.proto (its Go bindings ship in the module at gen/gortexa/ai/v1) and wire mcp.NewBridge to 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.

When developing the framework itself, use make:

Terminal window
make bootstrap # install the pinned toolchain
make gen # buf lint → breaking → generate
make test # go test -race
make run # start the sample server (injects a local dev JWT secret)
make dev # docker compose with Jaeger, Grafana, Prometheus
  • 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.