The Framework
From zero to running web application. Every step uses standard Go.
Install and Run
Download the binary from the download page, or install with Go. Requires Go 1.25+.
# Download and install
tar -xzf congo-*.tar.gz
sudo mv congo /usr/local/bin/
# Create a project and run it
congo init myapp
cd myapp
congo dev
Open localhost:5000 in your browser — you'll see a welcome page.
Edit any Go or template file and the server rebuilds automatically.
This creates a directory with the full framework vendored inside:
myapp/
internal/ Framework source (yours to read and modify)
application/ HTTP server, routing, controllers, templates
database/ ORM, auto-migration, SQLite/LibSQL engines
router/ Domain routing, TLS, middleware composition
security/ Nonce, headers, CSP
frontend/ React islands, esbuild, HMR
assistant/ AI chat, streaming, tool calling
platform/ Cloud server management, Docker, SSH
web/
controllers/ Your request handlers
models/ Your data models
views/ Your HTML templates
layouts/ Page layouts
partials/ Reusable components
static/ CSS, JS, images
main.go Entry point
go.mod
Dockerfile
CLAUDE.md AI context (generated by congo claude)
The framework lives in internal/. It's regular Go code —
open any file and read it.
Add a Controller
Controllers handle HTTP requests and expose methods to templates. Create web/controllers/todos.go:
package controllers
import (
"net/http"
"myapp/internal/application"
"myapp/web/models"
)
func Todos() (string, *TodosController) {
return "todos", &TodosController{}
}
type TodosController struct {
application.BaseController
}
func (c *TodosController) Setup(app *application.App) {
c.BaseController.Setup(app)
app.Handle("GET /todos", app.Serve("todos.html", nil))
app.Handle("POST /todos", app.Method(c, "Create", nil))
}
// Value receiver creates a copy — each request gets its own state.
func (c TodosController) Handle(r *http.Request) application.Controller {
c.Request = r
return &c
}
// Public methods are callable from templates: {{todos.All}}
func (c *TodosController) All() []*models.Todo {
items, _ := models.Todos.All()
return items
}
func (c *TodosController) Create(w http.ResponseWriter, r *http.Request) {
todo := &models.Todo{Title: r.FormValue("title")}
models.Todos.Insert(todo)
c.Redirect(w, r, "/todos")
}
Register it in web/main.go:
router.Listen(
router.WithLogger(),
security.New(security.WithNonce(), security.WithHeaders()),
application.New(views,
application.WithController(controllers.Home()),
application.WithController(controllers.Todos()),
),
)
The factory function returns a name and controller. The name becomes the template namespace —
todos.All calls the All() method.
Add a Model
Models are Go structs. The ORM creates tables, migrates schemas, and provides type-safe CRUD.
Create web/models/todo.go:
package models
import "myapp/internal/database"
type Todo struct {
database.Model
Title string
Done bool
}
var Todos = database.Manage(DB, new(Todo))
That's it. The table is created on startup. Columns are added automatically when you add struct fields.
database.Model provides ID, CreatedAt, and UpdatedAt.
// Insert — generates UUID, sets timestamps
id, err := models.Todos.Insert(&models.Todo{Title: "Ship it"})
// Get by ID
todo, err := models.Todos.Get(id)
// Search with SQL (PascalCase column names)
done, err := models.Todos.Search("WHERE Done = ?", true)
// Update — auto-updates UpdatedAt
todo.Done = true
err = models.Todos.Update(todo)
// Delete
err = models.Todos.Delete(todo)
Write a View
Views are standard Go html/template files with HTMX attributes.
Create web/views/todos.html:
{{template "main.html" .}}
{{define "content"}}
<div class="container mx-auto px-8 py-16 max-w-2xl">
<h1 class="text-3xl font-bold mb-8">Todos</h1>
<form hx-post="/todos" hx-target="body" class="flex gap-2 mb-8">
<input name="title" class="input input-bordered flex-1"
placeholder="What needs doing?" required />
<button class="btn btn-primary">Add</button>
</form>
{{range todos.All}}
<div class="flex items-center gap-3 py-2">
<span>{{.Title}}</span>
</div>
{{end}}
</div>
{{end}}
Controller methods like todos.All are called directly in templates.
HTMX handles form submissions and page updates without JavaScript.
DaisyUI
provides ready-made components — buttons, forms, cards — just add class names.
Write Tests
Tests get a fresh in-memory database automatically — no setup needed:
package models_test
import (
"testing"
"myapp/web/models"
)
func TestTodoInsert(t *testing.T) {
id, err := models.Todos.Insert(&models.Todo{Title: "Ship it"})
if err != nil {
t.Fatal(err)
}
todo, err := models.Todos.Get(id)
if err != nil {
t.Fatal(err)
}
if todo.Title != "Ship it" {
t.Errorf("got %q, want %q", todo.Title, "Ship it")
}
}
congo test # run all tests
congo test ./web/models/... # test models only
External dependencies have mock providers built in — assistant/providers/mock
for AI and platform/providers/mock for infrastructure.
AI-Assisted Development
congo claude
Launches Claude Code with the full framework reference injected. The AI knows the controller pattern, the model API, the template conventions. It writes code that fits because the framework taught it how.
Build and Deploy
congo build # single binary
congo launch # build, ship, deploy
congo build compiles your app into a single binary.
congo launch builds a Docker image, ships it to your server,
and starts it with health checks and automatic rollback.
Your project has three declarations: models/db.go declares
what the system remembers (data). main.go declares what the
system does (application). Infrastructure declares where the system lives — same pattern,
same functional options, different scope.
Generational Development
Every Congo binary carries the complete source tree inside it:
congo source ./my-framework
cd my-framework
go build -o my-cli ./cmd
Take the source, modify it, ship your own version.
When someone runs my-cli source,
they get your fork — and can fork it again.
Framework Packages
Seven packages. Four foundations with no framework dependencies. Three composites
that build on them. Use any combination — exclude what you don't need with
--no-frontend, --no-assistant, etc.
Foundations
Composites
Stay Updated
Get notified about new releases and updates.