FossilRepo

fossilrepo / bootstrap.md
1
# fossilrepo -- bootstrap
2
3
This is the primary conventions document. All agent shims (`CLAUDE.md`, `AGENTS.md`) point here.
4
5
An agent given this document and a business requirement should be able to generate correct, idiomatic code without exploring the codebase.
6
7
---
8
9
## What is fossilrepo
10
11
Omnibus-style installer for a self-hosted Fossil forge. One command gets you a full-stack code hosting platform: VCS, issues, wiki, timeline, web UI, SSL, and continuous backups -- all powered by Fossil SCM.
12
13
Think GitLab Omnibus, but for Fossil.
14
15
---
16
17
## Why Fossil
18
19
A Fossil repo is a single SQLite file. It contains the full VCS history, issue tracker, wiki, forum, and timeline. No external services. No rate limits. Portable -- hand the file to someone and they have everything.
20
21
For teams running CI agents or automation:
22
- Agents commit, file tickets, and update the wiki through one CLI and one protocol
23
- No API rate limits when many agents are pushing simultaneously
24
- The `.fossil` file IS the project artifact -- a self-contained archive
25
- Litestream replicates it to S3 continuously -- backup and point-in-time recovery for free
26
27
Fossil also has a built-in web UI (skinnable), autosync, peer-to-peer sync, and unversioned content storage (like Git LFS but built-in).
28
29
---
30
31
## What fossilrepo Does
32
33
fossilrepo packages everything needed to run a production Fossil server into one installable unit:
34
35
- **Fossil server** -- serves all repos from a single process
36
- **Caddy** -- SSL termination, subdomain-per-repo routing (`reponame.your-domain.com`)
37
- **Litestream** -- continuous SQLite replication to S3/MinIO (backup + point-in-time recovery)
38
- **CLI** -- repo lifecycle management (create, list, delete) and sync tooling
39
- **Sync bridge** -- mirror Fossil repos to GitHub/GitLab as downstream read-only copies
40
41
New project = `fossil init`. No restart, no config change. Litestream picks it up automatically.
42
43
---
44
45
## Server Stack
46
47
```
48
Caddy (SSL termination, routing, subdomain per repo)
49
+-- fossil server --repolist /data/repos/
50
+-- /data/repos/
51
|-- projecta.fossil
52
|-- projectb.fossil
53
+-- ...
54
55
Litestream -> S3/MinIO (continuous replication, point-in-time recovery)
56
```
57
58
One binary serves all repos. The whole platform is: repo creation + subdomain provisioning + Litestream config.
59
60
### Sync Bridge
61
62
Mirrors Fossil to GitHub/GitLab as a downstream copy. Fossil is the source of truth.
63
64
Maps:
65
- Fossil commits -> Git commits
66
- Fossil tickets -> GitHub/GitLab Issues (optional, configurable)
67
- Fossil wiki -> repo docs (optional, configurable)
68
69
Triggered on demand or on schedule.
70
71
---
72
73
## Architecture
74
75
```
76
fossilrepo/
77
|-- config/ # Django settings, URLs, Celery
78
|-- core/ # Base models, permissions, middleware
79
|-- accounts/ # Session-based auth
80
|-- organization/ # Org + member management
81
|-- docker/ # Fossil-specific: Caddyfile, litestream.yml
82
|-- templates/ # HTMX templates
83
|-- _old_fossilrepo/ # Original server/sync/cli code (being ported)
84
+-- docs/ # Architecture guides
85
```
86
87
---
88
89
## What's Already Built
90
91
| Layer | What's there |
92
|---|---|
93
| Auth | Session-based auth (accounts), login/logout views with templates, rate limiting |
94
| Data | Postgres 16, `Tracking` base model (version, created/updated/deleted by+at, soft deletes, history) |
95
| API | Django views returning HTML (full pages + HTMX partials) |
96
| Permissions | Group-based via `P` enum, checked in every view |
97
| Async | Celery worker + beat, Redis broker |
98
| Admin | Django Admin with `BaseCoreAdmin` (import/export, tracking fields) |
99
| Infra | Docker Compose: postgres, redis, celery-worker, celery-beat, mailpit |
100
| CI | GitHub Actions: lint (Ruff) + tests (Postgres + Redis services) |
101
| Seed | `python manage.py seed` creates admin/viewer users, sample data |
102
| Frontend | HTMX 2.0 + Alpine.js 3 + Tailwind CSS, server-rendered templates |
103
104
---
105
106
## App Structure
107
108
| App | Purpose |
109
|---|---|
110
| `config` | Django settings, URLs, Celery configuration |
111
| `core` | Base models (Tracking, BaseCoreModel), admin (BaseCoreAdmin), permissions (P enum), middleware |
112
| `accounts` | Session-based authentication: login/logout views with rate limiting |
113
| `organization` | Organization + OrganizationMember models |
114
| `testdata` | `seed` management command for development data |
115
116
---
117
118
## Conventions
119
120
### Models
121
122
All business models inherit from one of:
123
124
**`Tracking`** (abstract) -- audit trails:
125
```python
126
from core.models import Tracking
127
128
class Invoice(Tracking):
129
amount = models.DecimalField(...)
130
```
131
Provides: `version` (auto-increments), `created_at/by`, `updated_at/by`, `deleted_at/by`, `history` (simple_history).
132
133
**`BaseCoreModel(Tracking)`** (abstract) -- named entities:
134
```python
135
from core.models import BaseCoreModel
136
137
class Project(BaseCoreModel):
138
visibility = models.CharField(...)
139
```
140
Adds: `guid` (UUID), `name`, `slug` (auto-generated, unique), `description`.
141
142
**Soft deletes:** call `obj.soft_delete(user=request.user)`, never `.delete()`.
143
144
**ActiveManager:** Use `objects` (excludes deleted) for queries, `all_objects` for admin.
145
146
---
147
148
### Views (HTMX Pattern)
149
150
Views return full pages for normal requests, HTMX partials for `HX-Request`:
151
152
```python
153
@login_required
154
def project_list(request):
155
P.PROJECT_VIEW.check(request.user)
156
projects = Project.objects.all()
157
158
if request.headers.get("HX-Request"):
159
return render(request, "projects/partials/project_table.html", {"projects": projects})
160
161
return render(request, "projects/project_list.html", {"projects": projects})
162
```
163
164
**URL patterns** follow CRUD convention:
165
```python
166
urlpatterns = [
167
path("", views.project_list, name="list"),
168
path("create/", views.project_create, name="create"),
169
path("<slug:slug>/", views.project_detail, name="detail"),
170
path("<slug:slug>/edit/", views.project_update, name="update"),
171
path("<slug:slug>/delete/", views.project_delete, name="delete"),
172
]
173
```
174
175
---
176
177
### Permissions
178
179
Group-based. Never user-based. Checked in every view.
180
181
```python
182
from core.permissions import P
183
184
P.PROJECT_VIEW.check(request.user) # raises PermissionDenied if denied
185
P.PROJECT_ADD.check(request.user, raise_error=False) # returns False instead
186
```
187
188
Template guards:
189
```html
190
{% if perms.projects.view_project %}
191
<a href="{% url 'projects:list' %}">Projects</a>
192
{% endif %}
193
```
194
195
---
196
197
### Admin
198
199
All admin classes inherit `BaseCoreAdmin`:
200
```python
201
from core.admin import BaseCoreAdmin
202
203
@admin.register(Project)
204
class ProjectAdmin(BaseCoreAdmin):
205
list_display = ("name", "slug", "visibility", "created_at")
206
search_fields = ("name", "slug")
207
```
208
209
`BaseCoreAdmin` provides: audit fields as readonly, `created_by`/`updated_by` auto-set, import/export.
210
211
---
212
213
### Templates
214
215
- `base.html` -- layout with HTMX, Alpine.js, Tailwind CSS, CSRF injection, messages
216
- `includes/nav.html` -- navigation bar with permission guards
217
- `{app}/partials/*.html` -- HTMX partial templates (no `{% extends %}`)
218
- CSRF token sent with all HTMX requests via `htmx:configRequest` event
219
220
Alpine.js patterns for client-side interactivity:
221
```html
222
<div x-data="{ open: false }">
223
<button @click="open = !open">Toggle</button>
224
<div x-show="open" x-transition>Content</div>
225
</div>
226
```
227
228
---
229
230
### Tests
231
232
pytest + real Postgres. Assert against database state.
233
234
```python
235
@pytest.mark.django_db
236
class TestProjectCreate:
237
def test_create_saves_project(self, admin_client, admin_user, org):
238
response = admin_client.post(reverse("projects:create"), {
239
"name": "New App", "visibility": "private", ...
240
})
241
assert response.status_code == 302
242
project = Project.objects.get(name="New App")
243
assert project.created_by == admin_user
244
245
def test_create_denied_for_viewer(self, viewer_client):
246
response = viewer_client.get(reverse("projects:create"))
247
assert response.status_code == 403
248
```
249
250
Both allowed AND denied permission cases for every endpoint.
251
252
---
253
254
### Code Style
255
256
| Tool | Config |
257
|------|--------|
258
| Ruff (lint + format) | `pyproject.toml`, line length 140 |
259
| Import sorting | Ruff isort rules |
260
| Python version | 3.12+ |
261
262
Run `ruff check .` and `ruff format --check .` before committing.
263
264
---
265
266
## Adding a New App
267
268
```bash
269
# 1. Create the app
270
python manage.py startapp myapp
271
272
# 2. Add to INSTALLED_APPS in config/settings.py
273
274
# 3. Create models inheriting Tracking or BaseCoreModel
275
276
# 4. Create migrations
277
python manage.py makemigrations
278
279
# 5. Create admin (inherit BaseCoreAdmin)
280
281
# 6. Create views with @login_required + P.PERMISSION.check()
282
283
# 7. Create URL patterns (list, detail, create, update, delete)
284
285
# 8. Create templates (full page + HTMX partials)
286
287
# 9. Add permission entries to core/permissions.py P enum
288
289
# 10. Write tests (allowed + denied)
290
python -m pytest --cov -v
291
```
292
293
---
294
295
## Ports (local Docker)
296
297
| Service | URL |
298
|---|---|
299
| Django | http://localhost:8000 |
300
| Django Admin | http://localhost:8000/admin/ |
301
| Health | http://localhost:8000/health/ |
302
| Mailpit | http://localhost:8025 |
303
| Postgres | localhost:5432 |
304
| Redis | localhost:6379 |
305
306
---
307
308
## Common Commands
309
310
```bash
311
make up # Start the stack
312
make build # Build and start
313
make down # Stop the stack
314
make migrate # Run migrations
315
make migrations # Create migrations
316
make seed # Load dev fixtures
317
make test # Run tests with coverage
318
make lint # Run Ruff check + format
319
make superuser # Create Django superuser
320
make shell # Shell into container
321
make logs # Tail Django logs
322
```
323
324
---
325
326
## Platform Vision (fossilrepos.com)
327
328
GitLab model:
329
- **Self-hosted** -- open source, run it yourself. fossilrepo is the tool.
330
- **Managed** -- fossilrepos.com, hosted for you. Subdomain per repo, modern UI, billing.
331
332
The platform is Fossil's built-in web UI with a modern skin + thin API wrapper + authentication. Not a rewrite -- Fossil already does the hard parts. The value is the hosting and UX polish.
333
334
Not being built yet -- get the self-hosted tool right first.
335
336
---
337
338
## License
339
340
MIT.
341

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button