framework.html
378 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
368
369
370
371
372
373
374
375
376
377
378
{{template "main.html" .}}
{{define "title"}}Framework — Congo{{end}}
{{define "description"}}Build Go web applications with Congo. Controllers, models, views, testing, deployment — all with vendored source you own.{{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">The Framework</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">
<!-- Getting Started -->
<section>
<div class="text-accent text-sm font-mono mb-4">01 — Get Started</div>
<h2 class="text-xl font-bold tracking-tight mb-4">Install and Run</h2>
<p class="text-body leading-relaxed mb-5">
Download the binary from the <a href="/download" class="text-accent hover:text-bright transition-colors" hx-boost="true">download page</a>,
or install with Go. Requires <a href="https://go.dev/dl/" class="text-accent hover:text-bright transition-colors" target="_blank" rel="noopener">Go 1.25+</a>.
</p>
<div class="code-block mb-5">
<pre><code class="language-bash"># 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</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.
Edit any Go or template file and the server rebuilds automatically.
</p>
<p class="text-body leading-relaxed mb-5">
This creates a 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, 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)</code></pre>
</div>
<p class="text-body leading-relaxed">
The framework lives in <code class="text-accent">internal/</code>. It's regular Go code —
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">02 — 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)
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")
}</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">router.Listen(
router.WithLogger(),
security.New(security.WithNonce(), security.WithHeaders()),
application.New(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 <code>app.Handle</code>?</strong> App embeds Go's <code>*http.ServeMux</code> — so
<code>app.Handle</code> and <code>app.HandleFunc</code> are inherited standard library methods,
not framework abstractions. No custom router. No wrapper. Just Go.</p>
</div>
<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, 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">03 — 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>
<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">04 — 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 JavaScript.
<a href="https://daisyui.com/components/" class="text-accent hover:text-bright transition-colors" target="_blank" rel="noopener">DaisyUI</a>
provides ready-made components — buttons, forms, cards — just add class names.
</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. 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">05 — Testing</div>
<h2 class="text-xl font-bold tracking-tight mb-4">Write Tests</h2>
<p class="text-body leading-relaxed mb-5">
Tests get a fresh in-memory database automatically — no setup needed:
</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">congo test # run all tests
congo test ./web/models/... # test models only</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 and <code class="text-accent">platform/providers/mock</code> for infrastructure.
</p>
</section>
<!-- AI -->
<section class="section-divide pt-16">
<div class="text-accent text-sm font-mono mb-4">06 — 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">
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.
</p>
</section>
<!-- Deploy -->
<section class="section-divide pt-16">
<div class="text-accent text-sm font-mono mb-4">07 — 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">congo build # single binary
congo launch # build, ship, deploy</code></pre>
</div>
<p class="text-body leading-relaxed mb-5">
<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.
</p>
<p class="text-body leading-relaxed">
Your project has three declarations: <code class="text-accent">models/db.go</code> declares
what the system remembers (data). <code class="text-accent">main.go</code> declares what the
system does (application). Infrastructure declares where the system lives — same pattern,
same functional options, different scope.
</p>
</section>
<!-- Fork -->
<section class="section-divide pt-16">
<div class="text-accent text-sm font-mono mb-4">08 — Fork</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:
</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">
Take the source, modify it, ship your own version.
When someone runs <code class="text-accent">my-cli source</code>,
they get your fork — and can fork it again.
</p>
<div class="quote-bar">
<p>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">
Seven packages. Four foundations with no framework dependencies. Three composites
that build on them. Use any combination — exclude what you don't need with
<code class="text-accent">--no-frontend</code>, <code class="text-accent">--no-assistant</code>, etc.
</p>
<p class="text-body text-xs text-muted mb-4 uppercase tracking-wider font-semibold">Foundations</p>
<div class="space-y-0 mb-6" hx-boost="true">
<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">Data storage — Collection[T], auto-migration, SQLite/LibSQL engines</span>
</a>
<a href="/source/pkg/router" class="table-row flex items-center justify-between py-4 px-2">
<span class="font-mono text-accent text-sm">router</span>
<span class="text-body text-sm">Networking — domain proxy, TLS autocert, logging, server lifecycle</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 integration — chat, streaming, tool calling, 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">Infrastructure — servers, Docker, SSH, cloud providers</span>
</a>
</div>
<p class="text-body text-xs text-muted mb-4 uppercase tracking-wider font-semibold">Composites</p>
<div class="space-y-0" hx-boost="true">
<a href="/source/pkg/security" class="table-row flex items-center justify-between py-4 px-2">
<span class="font-mono text-accent text-sm">security</span>
<span class="text-body text-sm">HTTP security — nonce, CSP, rate limiting, JWT, bcrypt</span>
</a>
<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">MVC grammar — App embeds ServeMux, controllers, views, options</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">Client rendering — React islands, esbuild, HMR</span>
</a>
</div>
</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}}