# Quickstart

Source: https://gortexa.sheng.page/en/quickstart/

This quickstart installs the pinned toolchain, scaffolds a project, generates a CRUD API from proto, and verifies all three protocols — about ten minutes on a machine that already has Go 1.21+.

## Install

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

```bash
GORTEXA_VERSION="v0.28.3" # 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.

Note: the `go.mod` of v0.28.0 and v0.28.1 carries a `replace` directive, and Go refuses `go install …@` either of them ("contains one or more replace directives"). Pin the CLI to v0.28.2 or later; v0.27.5 installs too, but its scaffolds lack the namespace and api-module behaviour this page describes.

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

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

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

The full command set: `create` (new project), `gen` (generate an API), `regen` (regenerate after proto edits), `run` (start the dev server), `export --format mcp|openai|gemini [--out file]` (export AI tool schemas), `tools install` (install the pinned toolchain), `tools sync` (re-pin one tool), `skills install` / `skills list` (AI-assistant skills), `doctor` (environment check), and `version`.

## Create a project

```bash
gortexa create myapp --module github.com/me/myapp \
  --repo https://github.com/yshengliao/gortexa.git --ref v0.28.3
cd myapp
# create writes no usable secret — it leaves the placeholder that refuses
# startup. Confirm it is there.
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. The sample lives in the project's own proto namespace: package `myapp.resource.v1` under `proto/myapp/resource/v1/`. The namespace is derived from the last element of the module path and recorded in `.gortexa/project.json`, which `gortexa doctor` prints.

**This is a runnable demo, not a data service**
The next gen command creates process-local in-memory logic; data disappears on restart and is not shared between replicas. Treat it as a contract and transport scaffold. A real service must add persistence, authorization, and real readiness. If the toolchain was installed from a framework checkout, validate it in the new project with gortexa doctor rather than assuming make bootstrap can reinstall it there.

## Generate your first API

```bash
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. The service lands under the project's namespace: package `myapp.billing.v1` in `proto/myapp/billing/v1/invoice.proto`, and gRPC and MCP see it as `myapp.billing.v1.InvoiceService`. After later proto edits, run `gortexa regen`. Overwriting an existing API needs `--force` — note it also implies `--allow-breaking`, skipping buf's breaking-change gate.

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

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.

```bash
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
(
  set -euo pipefail
  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.

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

```bash
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`:

```go
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:

```bash
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.

## Use as a module (no scaffold)

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

```bash
go get github.com/yshengliao/gortexa@latest
```

A minimal app — one h2c port with gRPC health, `/healthz`, `/readyz` — needs no code generation at all. `kernel.New` requires an explicit decision about the governance chain: pass `WithInterceptors` to install the eight stages, or `WithoutInterceptors()` to serve without them — omitting both fails at startup, because the framework will not let you make that choice by forgetting. The example below takes the second option, which is only safe because it serves nothing but health; switch to `WithInterceptors` before registering a real service:

```go
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()

	// defaults: h2c on :8080, graceful shutdown. WithoutInterceptors is a
	// deliberate opt-out: no recover, auth, rate limit or validation.
	app, err := kernel.New(kernel.WithoutInterceptors())
	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 companion module `github.com/yshengliao/gortexa/api`, import path `github.com/yshengliao/gortexa/api/gen/gortexa/ai/v1`, resolved along with the framework by `go get`) 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](https://gortexa.sheng.page/en/components/).

Note: since v0.28.0 a project created by `gortexa create` may also `go get` the framework. The `gortexa.ai.v1` descriptor is registered once, by the `github.com/yshengliao/gortexa/api` module both depend on, and the sample sits in the project's own namespace, so nothing collides. The project still carries its own copy of the framework source under its own module path, so importing `github.com/yshengliao/gortexa/...` as well links two independent copies of that code — reach for it only when you need something your copy does not have. Scaffolds from v0.27 and earlier have no such guarantee: both copies register `gortexa/ai/v1/annotations.proto` and the binary panics at init.

## Working inside the framework checkout

When developing the framework itself, use make:

```bash
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
```

## Next steps

- [Core concepts](https://gortexa.sheng.page/en/concepts/): how single-port multiplexing, the interceptor chain and the error model work.
- [Configuration](https://gortexa.sheng.page/en/configuration/): every config.yaml field and the environment-variable overrides.
- [Examples](https://gortexa.sheng.page/en/examples/): 11 runnable verification examples.
