guide.html
367 lines1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
{{template "main.html" .}}
{{define "title"}}Guide — Congo{{end}}
{{define "description"}}Build your first Go web application with Congo. Step-by-step guide covering installation, controllers, models, views, and deployment.{{end}}
{{define "content"}}
<div class="max-w-3xl mx-auto px-6 py-16">
<h1 class="text-3xl md:text-4xl font-bold tracking-tight mb-3">Build Your First App</h1>
<p class="text-body text-base mb-16">From zero to running web application. Every step uses standard Go.</p>
<div class="space-y-16">
<!-- Download -->
<section>
<div class="text-accent text-sm font-mono mb-4">01 — Download</div>
<h2 class="text-xl font-bold tracking-tight mb-4">Get Congo</h2>
<p class="text-body leading-relaxed mb-5">
Download a prebuilt binary for your platform from the <a href="/download" class="text-accent hover:text-bright transition-colors" hx-boost="true">download page</a>.
Unpack it and put it on your PATH:
</p>
<div class="code-block mb-5">
<pre><code class="language-bash"># macOS / Linux
tar -xzf congo-*.tar.gz
mv congo /usr/local/bin/
congo version</code></pre>
</div>
<p class="text-body text-sm leading-relaxed">
Requires <a href="https://go.dev/dl/" class="text-accent hover:text-bright transition-colors" target="_blank" rel="noopener">Go 1.25+</a> to build projects. The binary itself has no dependencies.
</p>
</section>
<!-- Create a Project -->
<section class="section-divide pt-16">
<div class="text-accent text-sm font-mono mb-4">02 — Scaffold</div>
<h2 class="text-xl font-bold tracking-tight mb-4">Create a Project</h2>
<div class="code-block mb-5">
<pre><code class="language-bash">congo init myapp
cd myapp
congo dev</code></pre>
</div>
<p class="text-body leading-relaxed mb-5">
Open <code class="text-accent">localhost:5000</code> in your browser — you'll see a welcome page with live reload.
Edit any template and the browser updates automatically.
</p>
<p class="text-body leading-relaxed mb-5">
This creates a new directory with the full framework vendored inside:
</p>
<div class="code-block mb-5">
<pre><code>myapp/
internal/ Framework source (yours to read and modify)
application/ HTTP server, routing, templates, middleware
database/ ORM, auto-migration, SQLite/LibSQL engines
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)</code></pre>
</div>
<p class="text-body leading-relaxed">
The framework lives in <code class="text-accent">internal/</code>. It's regular Go code.
No hidden magic, no code generation, no reflection tricks. Open any file and read it.
</p>
</section>
<!-- Controllers -->
<section class="section-divide pt-16">
<div class="text-accent text-sm font-mono mb-4">03 — Controllers</div>
<h2 class="text-xl font-bold tracking-tight mb-4">Add a Controller</h2>
<p class="text-body leading-relaxed mb-5">
Controllers handle HTTP requests and expose methods to templates. Create <code class="text-accent">web/controllers/todos.go</code>:
</p>
<div class="code-block mb-5">
<pre><code class="language-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)
http.Handle("GET /todos", app.Serve("todos.html", nil))
http.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")
}</code></pre>
</div>
<p class="text-body leading-relaxed mb-5">Register it in <code class="text-accent">web/main.go</code>:</p>
<div class="code-block mb-5">
<pre><code class="language-go">application.Serve(views,
application.WithController(controllers.Home()),
application.WithController(controllers.Todos()),
)</code></pre>
</div>
<p class="text-body leading-relaxed mb-5">
The factory function returns a name and controller. The name becomes the template namespace —
<code class="text-accent">todos.All</code> calls the <code>All()</code> method.
</p>
<div class="quote-bar">
<p><strong>Why value receiver on Handle()?</strong> Go copies the struct when you use a value receiver (<code>c TodosController</code> instead of <code>*TodosController</code>). Each HTTP request gets its own copy of the controller, so concurrent requests can't interfere with each other. All other methods use pointer receivers as normal.</p>
</div>
</section>
<!-- Models -->
<section class="section-divide pt-16">
<div class="text-accent text-sm font-mono mb-4">04 — Models</div>
<h2 class="text-xl font-bold tracking-tight mb-4">Add a Model</h2>
<p class="text-body leading-relaxed mb-5">
Models are Go structs. The ORM creates tables, migrates schemas, and provides type-safe CRUD.
Create <code class="text-accent">web/models/todo.go</code>:
</p>
<div class="code-block mb-5">
<pre><code class="language-go">package models
import "myapp/internal/database"
type Todo struct {
database.Model
Title string
Done bool
}
var Todos = database.Manage(DB, new(Todo))</code></pre>
</div>
<p class="text-body leading-relaxed mb-5">
That's it. The table is created on startup. Columns are added automatically when you add struct fields.
<code class="text-accent">database.Model</code> provides ID, CreatedAt, and UpdatedAt.
</p>
<div class="code-block mb-5">
<pre><code class="language-go">// 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)</code></pre>
</div>
<p class="text-body leading-relaxed mb-5">
IDs are always strings (UUIDs). Add indexes with <code class="text-accent">database.WithUniqueIndex</code> or <code class="text-accent">database.WithIndex</code>.
</p>
<div class="quote-bar">
<p><strong>Why PascalCase SQL?</strong> Column names match Go struct fields exactly — <code>Title</code> in the struct becomes <code>Title</code> in SQL. No mapping layer, no tags, no surprises. <code>WHERE Done = ?</code> reads the same as <code>todo.Done</code>.</p>
</div>
</section>
<!-- Views -->
<section class="section-divide pt-16">
<div class="text-accent text-sm font-mono mb-4">05 — Views</div>
<h2 class="text-xl font-bold tracking-tight mb-4">Write a View</h2>
<p class="text-body leading-relaxed mb-5">
Views are standard Go <code>html/template</code> files with HTMX attributes.
Create <code class="text-accent">web/views/todos.html</code>:
</p>
<div class="code-block mb-5">
<pre><code>{{"{{"}}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{{"}}"}}</code></pre>
</div>
<p class="text-body leading-relaxed mb-5">
Controller methods like <code class="text-accent">todos.All</code> are called directly in templates.
HTMX handles form submissions and page updates without writing JavaScript.
</p>
<div class="quote-bar">
<p><strong>Why filename only?</strong> Templates reference layouts and partials by filename — <code>{{"{{"}}template "main.html" .{{"}}"}}</code>, not by path. All templates are in a flat namespace, so you never need to remember directory structures. Move files around without updating references.</p>
</div>
</section>
<!-- Testing -->
<section class="section-divide pt-16">
<div class="text-accent text-sm font-mono mb-4">06 — Testing</div>
<h2 class="text-xl font-bold tracking-tight mb-4">Write Tests</h2>
<p class="text-body leading-relaxed mb-5">
Congo uses an in-memory SQLite database when no <code class="text-accent">DB_PATH</code> or <code class="text-accent">DB_URL</code>
is set — which means tests get a fresh database automatically. Test models with standard Go tests:
</p>
<div class="code-block mb-5">
<pre><code class="language-go">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")
}
}</code></pre>
</div>
<div class="code-block mb-5">
<pre><code class="language-bash">go test ./web/models/...</code></pre>
</div>
<p class="text-body leading-relaxed">
External dependencies have mock providers built in — <code class="text-accent">assistant/providers/mock</code>
for AI features and <code class="text-accent">platform/providers/mock</code> for infrastructure.
No external services needed to run your test suite.
</p>
</section>
<!-- AI -->
<section class="section-divide pt-16">
<div class="text-accent text-sm font-mono mb-4">07 — AI</div>
<h2 class="text-xl font-bold tracking-tight mb-4">AI-Assisted Development</h2>
<div class="code-block mb-5">
<pre><code class="language-bash">congo claude</code></pre>
</div>
<p class="text-body leading-relaxed">
This launches Claude Code with a complete framework reference injected into the session.
The AI knows the controller pattern, the model API, the template conventions, and the HTMX patterns.
It writes code that fits your project because the framework taught it how.
</p>
</section>
<!-- Build and Deploy -->
<section class="section-divide pt-16">
<div class="text-accent text-sm font-mono mb-4">08 — Deploy</div>
<h2 class="text-xl font-bold tracking-tight mb-4">Build and Deploy</h2>
<div class="code-block mb-5">
<pre><code class="language-bash"># Build a production binary
congo build
# Deploy to your server
congo launch</code></pre>
</div>
<p class="text-body leading-relaxed">
<code class="text-accent">congo build</code> compiles your app into a single binary.
<code class="text-accent">congo launch</code> builds a Docker image, ships it to your server,
and starts it with health checks and automatic rollback. Your infrastructure is defined in
<code class="text-accent">infra.json</code> — servers, volumes, services, all in one file.
</p>
</section>
<!-- Source -->
<section class="section-divide pt-16">
<div class="text-accent text-sm font-mono mb-4">09 — Fork the Framework</div>
<h2 class="text-xl font-bold tracking-tight mb-4">Generational Development</h2>
<p class="text-body leading-relaxed mb-5">
Every Congo binary carries the complete source tree inside it. Run <code class="text-accent">congo source</code> to extract
a buildable copy — the CLI, the framework packages, the scaffold templates, everything.
</p>
<div class="code-block mb-5">
<pre><code class="language-bash">congo source ./my-framework
cd my-framework
go build -o my-cli ./cmd</code></pre>
</div>
<p class="text-body leading-relaxed mb-5">
This is how Congo is meant to evolve. Take the source, modify it, ship your own version.
Your CLI carries your changes inside it. When someone runs <code class="text-accent">my-cli source</code>,
they get your fork — and can fork it again. Each generation inherits and builds on the last.
</p>
<div class="quote-bar">
<p>Software that reproduces itself. Every binary is a seed for the next version. No central repository. No permission needed. Fork it, improve it, pass it on.</p>
</div>
</section>
<!-- Packages -->
<section id="packages" class="section-divide pt-16">
<div class="text-accent text-sm font-mono mb-4">Packages</div>
<h2 class="text-xl font-bold tracking-tight mb-5">Framework Packages</h2>
<p class="text-body leading-relaxed mb-6">
Congo includes five packages. Use what you need, exclude what you don't with flags like <code class="text-accent">--no-frontend</code> or <code class="text-accent">--no-database</code>.
</p>
<div class="space-y-0" hx-boost="true">
<a href="/source/pkg/application" class="table-row flex items-center justify-between py-4 px-2">
<span class="font-mono text-accent text-sm">application</span>
<span class="text-body text-sm">HTTP server, routing, controllers, templates, middleware, email</span>
</a>
<a href="/source/pkg/database" class="table-row flex items-center justify-between py-4 px-2">
<span class="font-mono text-accent text-sm">database</span>
<span class="text-body text-sm">ORM with auto-migration, SQLite/LibSQL engines, type-safe collections</span>
</a>
<a href="/source/pkg/frontend" class="table-row flex items-center justify-between py-4 px-2">
<span class="font-mono text-accent text-sm">frontend</span>
<span class="text-body text-sm">React islands with esbuild, hot module replacement, auto-mounting</span>
</a>
<a href="/source/pkg/assistant" class="table-row flex items-center justify-between py-4 px-2">
<span class="font-mono text-accent text-sm">assistant</span>
<span class="text-body text-sm">AI chat with streaming, tool calling, OpenAI and Anthropic providers</span>
</a>
<a href="/source/pkg/platform" class="table-row flex items-center justify-between py-4 px-2">
<span class="font-mono text-accent text-sm">platform</span>
<span class="text-body text-sm">Cloud infrastructure, Docker, SSH, server provisioning, deployment</span>
</a>
</div>
<p class="text-body text-sm leading-relaxed mt-6">
Every package is linked to its source. Read the implementation, understand the abstractions, modify them if you want.
</p>
</section>
</div>
<div class="mt-16 text-center">
<a href="/philosophy" class="btn-glow" hx-boost="true">Why these choices?</a>
</div>
<div class="mt-16 max-w-md mx-auto">
{{template "mailing-list.html" .}}
</div>
</div>
{{end}}