FossilRepo

Rotate secrets, deploy.sh reads from .env.deploy, gitignore fixes

ragelink 2026-04-07 20:02 trunk
Commit 45192efa4fe1bfabbcdddd1121c312ede1c181e6a0c4729f7153c7d0a6ef950b
--- core/context_processors.py
+++ core/context_processors.py
@@ -22,11 +22,21 @@
2222
grouped_projects.append({"group": group, "projects": group_projects})
2323
grouped_ids.update(p.id for p in group_projects)
2424
2525
ungrouped_projects = [p for p in projects if p.id not in grouped_ids]
2626
27
+ # Split pages: product docs (known slugs) vs org knowledge base (user-created)
28
+ PRODUCT_DOC_SLUGS = {
29
+ "agentic-development", "api-reference", "architecture",
30
+ "administration", "setup-guide",
31
+ }
32
+ product_docs = [p for p in pages if p.slug in PRODUCT_DOC_SLUGS]
33
+ kb_pages = [p for p in pages if p.slug not in PRODUCT_DOC_SLUGS]
34
+
2735
return {
2836
"sidebar_projects": projects,
2937
"sidebar_grouped": grouped_projects,
3038
"sidebar_ungrouped": ungrouped_projects,
31
- "sidebar_pages": pages,
39
+ "sidebar_pages": pages, # Keep for backwards compat
40
+ "sidebar_product_docs": product_docs,
41
+ "sidebar_kb_pages": kb_pages,
3242
}
3343
--- core/context_processors.py
+++ core/context_processors.py
@@ -22,11 +22,21 @@
22 grouped_projects.append({"group": group, "projects": group_projects})
23 grouped_ids.update(p.id for p in group_projects)
24
25 ungrouped_projects = [p for p in projects if p.id not in grouped_ids]
26
 
 
 
 
 
 
 
 
27 return {
28 "sidebar_projects": projects,
29 "sidebar_grouped": grouped_projects,
30 "sidebar_ungrouped": ungrouped_projects,
31 "sidebar_pages": pages,
 
 
32 }
33
--- core/context_processors.py
+++ core/context_processors.py
@@ -22,11 +22,21 @@
22 grouped_projects.append({"group": group, "projects": group_projects})
23 grouped_ids.update(p.id for p in group_projects)
24
25 ungrouped_projects = [p for p in projects if p.id not in grouped_ids]
26
27 # Split pages: product docs (known slugs) vs org knowledge base (user-created)
28 PRODUCT_DOC_SLUGS = {
29 "agentic-development", "api-reference", "architecture",
30 "administration", "setup-guide",
31 }
32 product_docs = [p for p in pages if p.slug in PRODUCT_DOC_SLUGS]
33 kb_pages = [p for p in pages if p.slug not in PRODUCT_DOC_SLUGS]
34
35 return {
36 "sidebar_projects": projects,
37 "sidebar_grouped": grouped_projects,
38 "sidebar_ungrouped": ungrouped_projects,
39 "sidebar_pages": pages, # Keep for backwards compat
40 "sidebar_product_docs": product_docs,
41 "sidebar_kb_pages": kb_pages,
42 }
43
+15 -12
--- deploy.sh
+++ deploy.sh
@@ -1,28 +1,33 @@
11
#!/bin/bash
22
# deploy.sh — push local state to fossilrepo.io
33
#
44
# Usage: ./deploy.sh [message]
5
-# 1. Checks in all changes to Fossil
6
-# 2. Pushes to fossilrepo.io
7
-# 3. SSM deploys code + syncs DB + repos to EC2
5
+#
6
+# Requires .env.deploy with:
7
+# FOSSIL_REMOTE_URL=https://admin:[email protected]/projects/fossilrepo/fossil/xfer
8
+# EC2_INSTANCE_ID=i-xxxx
9
+# S3_BUCKET=dev-fossilrepo-storage
10
+# AWS_REGION=us-west-2
811
912
set -euo pipefail
1013
11
-REMOTE="https://admin:[email protected]/projects/fossilrepo/fossil/xfer"
12
-INSTANCE_ID="i-0013c73c6f5146181"
13
-S3_BUCKET="dev-fossilrepo-storage"
14
-AWS_REGION="us-west-2"
14
+if [[ ! -f .env.deploy ]]; then
15
+ echo "Missing .env.deploy -- copy .env.deploy.example and fill in your values"
16
+ exit 1
17
+fi
18
+
19
+source .env.deploy
1520
1621
MSG="${1:-Deploy $(date +%Y-%m-%d-%H%M)}"
1722
1823
echo "=== Fossil commit ==="
1924
fossil addremove 2>/dev/null || true
2025
fossil commit -m "$MSG" 2>&1 || echo "Nothing to commit"
2126
22
-echo "=== Fossil push to fossilrepo.io ==="
23
-fossil push "$REMOTE"
27
+echo "=== Fossil push ==="
28
+fossil push "$FOSSIL_REMOTE_URL"
2429
2530
echo "=== Sync repos to S3 ==="
2631
AWS_PROFILE=fossiladmin aws s3 sync repos/ "s3://${S3_BUCKET}/sync/repos/" --region "$AWS_REGION" --exclude "*.fossil-shm" --exclude "*.fossil-wal" 2>&1 | tail -3
2732
2833
echo "=== Sync DB to S3 ==="
@@ -32,17 +37,15 @@
3237
echo "=== Push code to git ==="
3338
git add -A && git commit -m "$MSG" 2>/dev/null || true
3439
git push origin main 2>&1 || echo "Git push failed (non-critical)"
3540
3641
echo "=== Deploy to EC2 ==="
37
-AWS_PROFILE=fossiladmin aws s3 cp /tmp/sync-to-cloud.sh "s3://${S3_BUCKET}/sync-to-cloud.sh" --region "$AWS_REGION" 2>/dev/null || true
3842
AWS_PROFILE=fossiladmin aws ssm send-command \
39
- --instance-ids "$INSTANCE_ID" \
43
+ --instance-ids "$EC2_INSTANCE_ID" \
4044
--document-name "AWS-RunShellScript" \
4145
--timeout-seconds 300 \
4246
--parameters "{\"commands\":[\"export HOME=/root && aws s3 cp s3://${S3_BUCKET}/sync-to-cloud.sh /tmp/sync-to-cloud.sh --region ${AWS_REGION} && bash /tmp/sync-to-cloud.sh 2>&1\"]}" \
4347
--region "$AWS_REGION" \
4448
--query "Command.CommandId" \
4549
--output text
4650
4751
echo "=== Deploy triggered ==="
48
-echo "Monitor: AWS_PROFILE=fossiladmin aws ssm list-commands --instance-id $INSTANCE_ID --region $AWS_REGION --max-results 1"
4952
5053
ADDED docs/api/agentic-development.md
5154
ADDED docs/api/reference.md
5255
ADDED docs/architecture/api-reference.md
--- deploy.sh
+++ deploy.sh
@@ -1,28 +1,33 @@
1 #!/bin/bash
2 # deploy.sh — push local state to fossilrepo.io
3 #
4 # Usage: ./deploy.sh [message]
5 # 1. Checks in all changes to Fossil
6 # 2. Pushes to fossilrepo.io
7 # 3. SSM deploys code + syncs DB + repos to EC2
 
 
 
8
9 set -euo pipefail
10
11 REMOTE="https://admin:[email protected]/projects/fossilrepo/fossil/xfer"
12 INSTANCE_ID="i-0013c73c6f5146181"
13 S3_BUCKET="dev-fossilrepo-storage"
14 AWS_REGION="us-west-2"
 
 
15
16 MSG="${1:-Deploy $(date +%Y-%m-%d-%H%M)}"
17
18 echo "=== Fossil commit ==="
19 fossil addremove 2>/dev/null || true
20 fossil commit -m "$MSG" 2>&1 || echo "Nothing to commit"
21
22 echo "=== Fossil push to fossilrepo.io ==="
23 fossil push "$REMOTE"
24
25 echo "=== Sync repos to S3 ==="
26 AWS_PROFILE=fossiladmin aws s3 sync repos/ "s3://${S3_BUCKET}/sync/repos/" --region "$AWS_REGION" --exclude "*.fossil-shm" --exclude "*.fossil-wal" 2>&1 | tail -3
27
28 echo "=== Sync DB to S3 ==="
@@ -32,17 +37,15 @@
32 echo "=== Push code to git ==="
33 git add -A && git commit -m "$MSG" 2>/dev/null || true
34 git push origin main 2>&1 || echo "Git push failed (non-critical)"
35
36 echo "=== Deploy to EC2 ==="
37 AWS_PROFILE=fossiladmin aws s3 cp /tmp/sync-to-cloud.sh "s3://${S3_BUCKET}/sync-to-cloud.sh" --region "$AWS_REGION" 2>/dev/null || true
38 AWS_PROFILE=fossiladmin aws ssm send-command \
39 --instance-ids "$INSTANCE_ID" \
40 --document-name "AWS-RunShellScript" \
41 --timeout-seconds 300 \
42 --parameters "{\"commands\":[\"export HOME=/root && aws s3 cp s3://${S3_BUCKET}/sync-to-cloud.sh /tmp/sync-to-cloud.sh --region ${AWS_REGION} && bash /tmp/sync-to-cloud.sh 2>&1\"]}" \
43 --region "$AWS_REGION" \
44 --query "Command.CommandId" \
45 --output text
46
47 echo "=== Deploy triggered ==="
48 echo "Monitor: AWS_PROFILE=fossiladmin aws ssm list-commands --instance-id $INSTANCE_ID --region $AWS_REGION --max-results 1"
49
50 DDED docs/api/agentic-development.md
51 DDED docs/api/reference.md
52 DDED docs/architecture/api-reference.md
--- deploy.sh
+++ deploy.sh
@@ -1,28 +1,33 @@
1 #!/bin/bash
2 # deploy.sh — push local state to fossilrepo.io
3 #
4 # Usage: ./deploy.sh [message]
5 #
6 # Requires .env.deploy with:
7 # FOSSIL_REMOTE_URL=https://admin:[email protected]/projects/fossilrepo/fossil/xfer
8 # EC2_INSTANCE_ID=i-xxxx
9 # S3_BUCKET=dev-fossilrepo-storage
10 # AWS_REGION=us-west-2
11
12 set -euo pipefail
13
14 if [[ ! -f .env.deploy ]]; then
15 echo "Missing .env.deploy -- copy .env.deploy.example and fill in your values"
16 exit 1
17 fi
18
19 source .env.deploy
20
21 MSG="${1:-Deploy $(date +%Y-%m-%d-%H%M)}"
22
23 echo "=== Fossil commit ==="
24 fossil addremove 2>/dev/null || true
25 fossil commit -m "$MSG" 2>&1 || echo "Nothing to commit"
26
27 echo "=== Fossil push ==="
28 fossil push "$FOSSIL_REMOTE_URL"
29
30 echo "=== Sync repos to S3 ==="
31 AWS_PROFILE=fossiladmin aws s3 sync repos/ "s3://${S3_BUCKET}/sync/repos/" --region "$AWS_REGION" --exclude "*.fossil-shm" --exclude "*.fossil-wal" 2>&1 | tail -3
32
33 echo "=== Sync DB to S3 ==="
@@ -32,17 +37,15 @@
37 echo "=== Push code to git ==="
38 git add -A && git commit -m "$MSG" 2>/dev/null || true
39 git push origin main 2>&1 || echo "Git push failed (non-critical)"
40
41 echo "=== Deploy to EC2 ==="
 
42 AWS_PROFILE=fossiladmin aws ssm send-command \
43 --instance-ids "$EC2_INSTANCE_ID" \
44 --document-name "AWS-RunShellScript" \
45 --timeout-seconds 300 \
46 --parameters "{\"commands\":[\"export HOME=/root && aws s3 cp s3://${S3_BUCKET}/sync-to-cloud.sh /tmp/sync-to-cloud.sh --region ${AWS_REGION} && bash /tmp/sync-to-cloud.sh 2>&1\"]}" \
47 --region "$AWS_REGION" \
48 --query "Command.CommandId" \
49 --output text
50
51 echo "=== Deploy triggered ==="
 
52
53 DDED docs/api/agentic-development.md
54 DDED docs/api/reference.md
55 DDED docs/architecture/api-reference.md
--- a/docs/api/agentic-development.md
+++ b/docs/api/agentic-development.md
@@ -0,0 +1,188 @@
1
+# Agentic Development
2
+
3
+FossilRepo is built for AI-assisted development at scale. Traditional Git forges impose rate limits that cripple agent workflows. FossilRepo eliminates these bottlenecks.
4
+
5
+## The Problem
6
+
7
+AI coding agents make dozens of API calls per task. On GitHub:
8
+- 5,000 API calls/hour limit (agents burn through this in minutes)
9
+- 30 search requests/minute
10
+- Webhook delivery delays
11
+- Actions queue congestion
12
+
13
+Multiple agents working in parallel hit rate limits within seconds.
14
+
15
+## The Solution
16
+
17
+```
18
+AI Agent (Claude Code, Cursor, etc.)
19
+ |
20
+ v
21
+FossilRepo MCP Server / API <-- zero rate limits
22
+ |
23
+ v
24
+Fossil repos (.fossil SQLite) <-- local disk, instant
25
+ |
26
+ v (scheduled, batched)
27
+Git Mirror --> GitHub <-- rate-limit-aware sync
28
+```
29
+
30
+Agents work against FossilRepo locally with no limits. Changes sync to GitHub on a schedule. GitHub becomes a downstream mirror, not the bottleneck.
31
+
32
+## Connecting AI Tools
33
+
34
+### MCP Server (Recommended)
35
+
36
+The MCP server gives AI tools native access to all FossilRepo capabilities.
37
+
38
+```bash
39
+pip install fossilrepo
40
+fossilrepo-mcp
41
+```
42
+
43
+Claude Code config:
44
+```json
45
+{
46
+ "mcpServers": {
47
+ "fossilrepo": {
48
+ "command": "fossilrepo-mcp"
49
+ }
50
+ }
51
+}
52
+```
53
+
54
+17 tools available: browse code, read files, search, manage tickets, view timeline/diffs/blame, create tickets, run SQL queries.
55
+
56
+### JSON API
57
+
58
+For tools without MCP support:
59
+```
60
+curl -H "Authorization: Bearer frp_abc123..." \
61
+ http://localhost:8000/projects/myproject/fossil/api/timeline
62
+```
63
+
64
+### Batch API
65
+
66
+Reduce round-trips by 25x:
67
+```json
68
+POST /api/batch
69
+{"requests": [
70
+ {"method": "GET", "path": "/api/timeline"},
71
+ {"method": "GET", "path": "/api/tickets", "params": {"status": "Open"}},
72
+ {"method": "GET", "path": "/api/wiki/Home"}
73
+]}
74
+```
75
+
76
+## The Agent Workflow
77
+
78
+### 1. Discover Work
79
+
80
+Browse open, unclaimed tickets:
81
+```
82
+GET /api/tickets/unclaimed
83
+```
84
+
85
+### 2. Claim a Ticket
86
+
87
+Atomic claiming prevents two agents from working on the same thing:
88
+```
89
+POST /api/tickets/<uuid>/claim
90
+{"agent_id": "claude-session-abc"}
91
+```
92
+Returns 200 if claimed, 409 if already taken by another agent.
93
+
94
+### 3. Create an Isolated Workspace
95
+
96
+Each agent gets its own Fossil branch and checkout directory:
97
+```
98
+POST /api/workspaces/create
99
+{"name": "fix-auth-bug", "agent_id": "claude-session-abc"}
100
+```
101
+
102
+No interference with other agents or the main branch.
103
+
104
+### 4. Do the Work
105
+
106
+Read code, understand context, make changes, commit. All via MCP tools or API:
107
+```
108
+POST /api/workspaces/fix-auth-bug/commit
109
+{"message": "Fix null check in auth middleware"}
110
+```
111
+
112
+### 5. Submit for Review
113
+
114
+```
115
+POST /api/reviews/create
116
+{
117
+ "title": "Fix null pointer in auth module",
118
+ "description": "The auth check was failing when...",
119
+ "diff": "--- a/src/auth.py\n+++ b/src/auth.py\n...",
120
+ "workspace": "fix-auth-bug"
121
+}
122
+```
123
+
124
+### 6. Review and Merge
125
+
126
+Another agent (or human) reviews:
127
+```
128
+POST /api/reviews/<id>/approve
129
+POST /api/reviews/<id>/merge
130
+```
131
+
132
+### 7. Release the Claim
133
+
134
+```
135
+POST /api/tickets/<uuid>/submit
136
+{"summary": "Fixed by closing the null check gap"}
137
+```
138
+
139
+## Real-Time Coordination
140
+
141
+### Server-Sent Events
142
+
143
+Agents subscribe to a live event stream instead of polling:
144
+```
145
+GET /api/events
146
+```
147
+
148
+Events:
149
+- `checkin` — new commits pushed
150
+- `claim` — ticket claimed or released
151
+- `workspace` — workspace created, merged, or abandoned
152
+
153
+### Multi-Agent Safety
154
+
155
+FossilRepo prevents agent collisions through:
156
+
157
+1. **Atomic ticket claiming** — database-level locking via `select_for_update`. Only one agent can claim a ticket.
158
+2. **Isolated workspaces** — each agent works on its own Fossil branch in its own checkout directory. No merge conflicts during work.
159
+3. **Code review gate** — changes must be reviewed (by human or another agent) before merging to trunk.
160
+4. **Branch protection** — protected branches block non-admin pushes. CI status checks can be required.
161
+5. **SSE events** — agents know what others are doing in real-time, avoiding duplicate work.
162
+
163
+## Comparison with GitHub
164
+
165
+| Feature | GitHub | FossilRepo |
166
+|---------|--------|------------|
167
+| API rate limit | 5,000/hour | Unlimited |
168
+| Search rate limit | 30/min | Unlimited |
169
+| Agent workspace | Shared branch | Isolated checkout |
170
+| Task claiming | None (race conditions) | Atomic (DB-locked) |
171
+| Batch API | None | 25 calls/request |
172
+| Real-time events | Webhooks (delayed) | SSE (instant) |
173
+| Code review | Pull request (heavyweight) | Lightweight API |
174
+| MCP support | No | 17 tools |
175
+| CI status API | Rate-limited | Unlimited |
176
+| Self-hosted | No | Yes |
177
+| Cost | Per-seat | Free (MIT) |
178
+
179
+## Why Fossil for Agents?
180
+
181
+Fossil's architecture is uniquely suited for agentic development:
182
+
183
+- **Single-file repos** — each `.fossil` file is a complete SQLite database. No complex storage, no git pack files, no network dependencies.
184
+- **Built-in everything** — tickets, wiki, forum, technotes all in one file. Agents manage the full development lifecycle without switching tools.
185
+- **SQLite = instant reads** — FossilReader opens the file directly. No API calls, no HTTP, no rate limits. Microsecond latency.
186
+- **Offline-first** — works without internet. Sync to GitHub when ready, on your schedule.
187
+- **Clone = complete backup** — `fossil clone` gives you everything: code, tickets, wiki, forum. One file, one copy.
188
+- **Branching without overhead** — Fossil branches are lightweight metadata, not separate directory trees. Creating 50 agent workspaces costs nothing.
--- a/docs/api/agentic-development.md
+++ b/docs/api/agentic-development.md
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/docs/api/agentic-development.md
+++ b/docs/api/agentic-development.md
@@ -0,0 +1,188 @@
1 # Agentic Development
2
3 FossilRepo is built for AI-assisted development at scale. Traditional Git forges impose rate limits that cripple agent workflows. FossilRepo eliminates these bottlenecks.
4
5 ## The Problem
6
7 AI coding agents make dozens of API calls per task. On GitHub:
8 - 5,000 API calls/hour limit (agents burn through this in minutes)
9 - 30 search requests/minute
10 - Webhook delivery delays
11 - Actions queue congestion
12
13 Multiple agents working in parallel hit rate limits within seconds.
14
15 ## The Solution
16
17 ```
18 AI Agent (Claude Code, Cursor, etc.)
19 |
20 v
21 FossilRepo MCP Server / API <-- zero rate limits
22 |
23 v
24 Fossil repos (.fossil SQLite) <-- local disk, instant
25 |
26 v (scheduled, batched)
27 Git Mirror --> GitHub <-- rate-limit-aware sync
28 ```
29
30 Agents work against FossilRepo locally with no limits. Changes sync to GitHub on a schedule. GitHub becomes a downstream mirror, not the bottleneck.
31
32 ## Connecting AI Tools
33
34 ### MCP Server (Recommended)
35
36 The MCP server gives AI tools native access to all FossilRepo capabilities.
37
38 ```bash
39 pip install fossilrepo
40 fossilrepo-mcp
41 ```
42
43 Claude Code config:
44 ```json
45 {
46 "mcpServers": {
47 "fossilrepo": {
48 "command": "fossilrepo-mcp"
49 }
50 }
51 }
52 ```
53
54 17 tools available: browse code, read files, search, manage tickets, view timeline/diffs/blame, create tickets, run SQL queries.
55
56 ### JSON API
57
58 For tools without MCP support:
59 ```
60 curl -H "Authorization: Bearer frp_abc123..." \
61 http://localhost:8000/projects/myproject/fossil/api/timeline
62 ```
63
64 ### Batch API
65
66 Reduce round-trips by 25x:
67 ```json
68 POST /api/batch
69 {"requests": [
70 {"method": "GET", "path": "/api/timeline"},
71 {"method": "GET", "path": "/api/tickets", "params": {"status": "Open"}},
72 {"method": "GET", "path": "/api/wiki/Home"}
73 ]}
74 ```
75
76 ## The Agent Workflow
77
78 ### 1. Discover Work
79
80 Browse open, unclaimed tickets:
81 ```
82 GET /api/tickets/unclaimed
83 ```
84
85 ### 2. Claim a Ticket
86
87 Atomic claiming prevents two agents from working on the same thing:
88 ```
89 POST /api/tickets/<uuid>/claim
90 {"agent_id": "claude-session-abc"}
91 ```
92 Returns 200 if claimed, 409 if already taken by another agent.
93
94 ### 3. Create an Isolated Workspace
95
96 Each agent gets its own Fossil branch and checkout directory:
97 ```
98 POST /api/workspaces/create
99 {"name": "fix-auth-bug", "agent_id": "claude-session-abc"}
100 ```
101
102 No interference with other agents or the main branch.
103
104 ### 4. Do the Work
105
106 Read code, understand context, make changes, commit. All via MCP tools or API:
107 ```
108 POST /api/workspaces/fix-auth-bug/commit
109 {"message": "Fix null check in auth middleware"}
110 ```
111
112 ### 5. Submit for Review
113
114 ```
115 POST /api/reviews/create
116 {
117 "title": "Fix null pointer in auth module",
118 "description": "The auth check was failing when...",
119 "diff": "--- a/src/auth.py\n+++ b/src/auth.py\n...",
120 "workspace": "fix-auth-bug"
121 }
122 ```
123
124 ### 6. Review and Merge
125
126 Another agent (or human) reviews:
127 ```
128 POST /api/reviews/<id>/approve
129 POST /api/reviews/<id>/merge
130 ```
131
132 ### 7. Release the Claim
133
134 ```
135 POST /api/tickets/<uuid>/submit
136 {"summary": "Fixed by closing the null check gap"}
137 ```
138
139 ## Real-Time Coordination
140
141 ### Server-Sent Events
142
143 Agents subscribe to a live event stream instead of polling:
144 ```
145 GET /api/events
146 ```
147
148 Events:
149 - `checkin` — new commits pushed
150 - `claim` — ticket claimed or released
151 - `workspace` — workspace created, merged, or abandoned
152
153 ### Multi-Agent Safety
154
155 FossilRepo prevents agent collisions through:
156
157 1. **Atomic ticket claiming** — database-level locking via `select_for_update`. Only one agent can claim a ticket.
158 2. **Isolated workspaces** — each agent works on its own Fossil branch in its own checkout directory. No merge conflicts during work.
159 3. **Code review gate** — changes must be reviewed (by human or another agent) before merging to trunk.
160 4. **Branch protection** — protected branches block non-admin pushes. CI status checks can be required.
161 5. **SSE events** — agents know what others are doing in real-time, avoiding duplicate work.
162
163 ## Comparison with GitHub
164
165 | Feature | GitHub | FossilRepo |
166 |---------|--------|------------|
167 | API rate limit | 5,000/hour | Unlimited |
168 | Search rate limit | 30/min | Unlimited |
169 | Agent workspace | Shared branch | Isolated checkout |
170 | Task claiming | None (race conditions) | Atomic (DB-locked) |
171 | Batch API | None | 25 calls/request |
172 | Real-time events | Webhooks (delayed) | SSE (instant) |
173 | Code review | Pull request (heavyweight) | Lightweight API |
174 | MCP support | No | 17 tools |
175 | CI status API | Rate-limited | Unlimited |
176 | Self-hosted | No | Yes |
177 | Cost | Per-seat | Free (MIT) |
178
179 ## Why Fossil for Agents?
180
181 Fossil's architecture is uniquely suited for agentic development:
182
183 - **Single-file repos** — each `.fossil` file is a complete SQLite database. No complex storage, no git pack files, no network dependencies.
184 - **Built-in everything** — tickets, wiki, forum, technotes all in one file. Agents manage the full development lifecycle without switching tools.
185 - **SQLite = instant reads** — FossilReader opens the file directly. No API calls, no HTTP, no rate limits. Microsecond latency.
186 - **Offline-first** — works without internet. Sync to GitHub when ready, on your schedule.
187 - **Clone = complete backup** — `fossil clone` gives you everything: code, tickets, wiki, forum. One file, one copy.
188 - **Branching without overhead** — Fossil branches are lightweight metadata, not separate directory trees. Creating 50 agent workspaces costs nothing.
--- a/docs/api/reference.md
+++ b/docs/api/reference.md
@@ -0,0 +1,180 @@
1
+# API Reference
2
+
3
+FossilRepo provides a JSON API for programmatic access and an MCP server for AI tool integration.
4
+
5
+## Authentication
6
+
7
+All API endpoints accept authentication via:
8
+
9
+1. **Bearer token** (recommended for automation):
10
+ ```
11
+ Authorization: Bearer frp_abc123...
12
+ ```
13
+ Tokens can be project-scoped (API Token) or user-scoped (Personal Access Token).
14
+
15
+2. **Session cookie** (for browser-based testing):
16
+ Log in via the web UI, then call API endpoints in the same browser session.
17
+
18
+## JSON API Endpoints
19
+
20
+Base URL: `/projects/<slug>/fossil/api/`
21
+
22
+### Project
23
+
24
+**GET /api/project** — Project metadata
25
+```json
26
+{"name": "FossilRepo", "slug": "fossilrepo", "description": "...", "visibility": "public", "star_count": 5}
27
+```
28
+
29
+### Timeline
30
+
31
+**GET /api/timeline** — Recent checkins
32
+- `?page=1` — Page number
33
+- `?per_page=25` — Items per page
34
+- `?branch=trunk` — Filter by branch
35
+
36
+### Tickets
37
+
38
+**GET /api/tickets** — Ticket list
39
+- `?status=Open` — Filter by status
40
+- `?page=1&per_page=25` — Pagination
41
+
42
+**GET /api/tickets/\<uuid\>** — Single ticket with comments
43
+
44
+**GET /api/tickets/unclaimed** — Tickets available for agent claiming
45
+
46
+**POST /api/tickets/\<uuid\>/claim** — Claim ticket for exclusive work
47
+```json
48
+{"agent_id": "claude-session-abc"}
49
+```
50
+
51
+**POST /api/tickets/\<uuid\>/release** — Release a claim
52
+
53
+**POST /api/tickets/\<uuid\>/submit** — Submit completed work
54
+```json
55
+{"summary": "Fixed by...", "workspace": "agent-fix-123"}
56
+```
57
+
58
+### Wiki
59
+
60
+**GET /api/wiki** — Wiki page list
61
+**GET /api/wiki/\<name\>** — Page content (raw + rendered HTML)
62
+
63
+### Branches and Tags
64
+
65
+**GET /api/branches** — All branches with open/closed status
66
+**GET /api/tags** — All tags
67
+
68
+### Releases
69
+
70
+**GET /api/releases** — Release list with assets
71
+
72
+### Search
73
+
74
+**GET /api/search?q=term** — Search across checkins, tickets, wiki
75
+
76
+### CI Status
77
+
78
+**POST /api/status** — Report CI build status (Bearer token required)
79
+```json
80
+{"checkin": "abc123", "context": "ci/tests", "state": "success", "description": "All tests passed", "target_url": "https://ci.example.com/123"}
81
+```
82
+
83
+**GET /api/status/\<checkin_uuid\>/badge.svg** — SVG status badge
84
+
85
+### Batch API
86
+
87
+**POST /api/batch** — Execute up to 25 API calls in one request
88
+```json
89
+{"requests": [
90
+ {"method": "GET", "path": "/api/timeline", "params": {"per_page": 5}},
91
+ {"method": "GET", "path": "/api/tickets", "params": {"status": "Open"}},
92
+ {"method": "GET", "path": "/api/wiki/Home"}
93
+]}
94
+```
95
+
96
+### Agent Workspaces
97
+
98
+**GET /api/workspaces** — List active workspaces
99
+**POST /api/workspaces/create** — Create isolated workspace
100
+```json
101
+{"name": "fix-auth-bug", "agent_id": "claude-abc", "description": "Fixing auth issue"}
102
+```
103
+**GET /api/workspaces/\<name\>** — Workspace details
104
+**POST /api/workspaces/\<name\>/commit** — Commit changes
105
+**POST /api/workspaces/\<name\>/merge** — Merge back to trunk
106
+**DELETE /api/workspaces/\<name\>/abandon** — Abandon workspace
107
+
108
+### Code Reviews
109
+
110
+**GET /api/reviews** — List reviews (filterable by status)
111
+**POST /api/reviews/create** — Submit code for review
112
+```json
113
+{"title": "Fix null pointer", "description": "...", "diff": "--- a/...", "files_changed": ["src/auth.py"]}
114
+```
115
+**GET /api/reviews/\<id\>** — Review with comments
116
+**POST /api/reviews/\<id\>/comment** — Add review comment
117
+**POST /api/reviews/\<id\>/approve** — Approve
118
+**POST /api/reviews/\<id\>/request-changes** — Request changes
119
+**POST /api/reviews/\<id\>/merge** — Merge approved review
120
+
121
+### Server-Sent Events
122
+
123
+**GET /api/events** — Real-time event stream
124
+```
125
+event: checkin
126
+data: {"uuid": "abc123", "user": "dev", "comment": "Fix bug"}
127
+
128
+event: claim
129
+data: {"ticket": "def456", "agent": "claude-abc", "status": "claimed"}
130
+
131
+event: workspace
132
+data: {"name": "fix-auth", "branch": "workspace/fix-auth", "status": "merged"}
133
+```
134
+
135
+## MCP Server
136
+
137
+The MCP (Model Context Protocol) server gives AI tools native access to FossilRepo.
138
+
139
+### Setup
140
+
141
+```bash
142
+pip install fossilrepo
143
+fossilrepo-mcp
144
+```
145
+
146
+### Claude Code Configuration
147
+
148
+```json
149
+{
150
+ "mcpServers": {
151
+ "fossilrepo": {
152
+ "command": "fossilrepo-mcp"
153
+ }
154
+ }
155
+}
156
+```
157
+
158
+### Available Tools
159
+
160
+| Tool | Description |
161
+|------|-------------|
162
+| list_projects | List all projects |
163
+| get_project | Project details with repo stats |
164
+| browse_code | List files in a directory |
165
+| read_file | Read file content |
166
+| get_timeline | Recent checkins (optional branch filter) |
167
+| get_checkin | Checkin detail with file changes |
168
+| search_code | Search across checkins, tickets, wiki |
169
+| list_tickets | List tickets (optional status filter) |
170
+| get_ticket | Ticket detail with comments |
171
+| create_ticket | Create a new ticket |
172
+| update_ticket | Update ticket status, add comment |
173
+| list_wiki_pages | List all wiki pages |
174
+| get_wiki_page | Read wiki page content |
175
+| list_branches | List all branches |
176
+| get_file_blame | Blame annotations for a file |
177
+| get_file_history | Commit history for a file |
178
+| sql_query | Run read-only SQL against Fossil SQLite |
179
+
180
+All tools accept a `slug` parameter to identify the project.
--- a/docs/api/reference.md
+++ b/docs/api/reference.md
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/docs/api/reference.md
+++ b/docs/api/reference.md
@@ -0,0 +1,180 @@
1 # API Reference
2
3 FossilRepo provides a JSON API for programmatic access and an MCP server for AI tool integration.
4
5 ## Authentication
6
7 All API endpoints accept authentication via:
8
9 1. **Bearer token** (recommended for automation):
10 ```
11 Authorization: Bearer frp_abc123...
12 ```
13 Tokens can be project-scoped (API Token) or user-scoped (Personal Access Token).
14
15 2. **Session cookie** (for browser-based testing):
16 Log in via the web UI, then call API endpoints in the same browser session.
17
18 ## JSON API Endpoints
19
20 Base URL: `/projects/<slug>/fossil/api/`
21
22 ### Project
23
24 **GET /api/project** — Project metadata
25 ```json
26 {"name": "FossilRepo", "slug": "fossilrepo", "description": "...", "visibility": "public", "star_count": 5}
27 ```
28
29 ### Timeline
30
31 **GET /api/timeline** — Recent checkins
32 - `?page=1` — Page number
33 - `?per_page=25` — Items per page
34 - `?branch=trunk` — Filter by branch
35
36 ### Tickets
37
38 **GET /api/tickets** — Ticket list
39 - `?status=Open` — Filter by status
40 - `?page=1&per_page=25` — Pagination
41
42 **GET /api/tickets/\<uuid\>** — Single ticket with comments
43
44 **GET /api/tickets/unclaimed** — Tickets available for agent claiming
45
46 **POST /api/tickets/\<uuid\>/claim** — Claim ticket for exclusive work
47 ```json
48 {"agent_id": "claude-session-abc"}
49 ```
50
51 **POST /api/tickets/\<uuid\>/release** — Release a claim
52
53 **POST /api/tickets/\<uuid\>/submit** — Submit completed work
54 ```json
55 {"summary": "Fixed by...", "workspace": "agent-fix-123"}
56 ```
57
58 ### Wiki
59
60 **GET /api/wiki** — Wiki page list
61 **GET /api/wiki/\<name\>** — Page content (raw + rendered HTML)
62
63 ### Branches and Tags
64
65 **GET /api/branches** — All branches with open/closed status
66 **GET /api/tags** — All tags
67
68 ### Releases
69
70 **GET /api/releases** — Release list with assets
71
72 ### Search
73
74 **GET /api/search?q=term** — Search across checkins, tickets, wiki
75
76 ### CI Status
77
78 **POST /api/status** — Report CI build status (Bearer token required)
79 ```json
80 {"checkin": "abc123", "context": "ci/tests", "state": "success", "description": "All tests passed", "target_url": "https://ci.example.com/123"}
81 ```
82
83 **GET /api/status/\<checkin_uuid\>/badge.svg** — SVG status badge
84
85 ### Batch API
86
87 **POST /api/batch** — Execute up to 25 API calls in one request
88 ```json
89 {"requests": [
90 {"method": "GET", "path": "/api/timeline", "params": {"per_page": 5}},
91 {"method": "GET", "path": "/api/tickets", "params": {"status": "Open"}},
92 {"method": "GET", "path": "/api/wiki/Home"}
93 ]}
94 ```
95
96 ### Agent Workspaces
97
98 **GET /api/workspaces** — List active workspaces
99 **POST /api/workspaces/create** — Create isolated workspace
100 ```json
101 {"name": "fix-auth-bug", "agent_id": "claude-abc", "description": "Fixing auth issue"}
102 ```
103 **GET /api/workspaces/\<name\>** — Workspace details
104 **POST /api/workspaces/\<name\>/commit** — Commit changes
105 **POST /api/workspaces/\<name\>/merge** — Merge back to trunk
106 **DELETE /api/workspaces/\<name\>/abandon** — Abandon workspace
107
108 ### Code Reviews
109
110 **GET /api/reviews** — List reviews (filterable by status)
111 **POST /api/reviews/create** — Submit code for review
112 ```json
113 {"title": "Fix null pointer", "description": "...", "diff": "--- a/...", "files_changed": ["src/auth.py"]}
114 ```
115 **GET /api/reviews/\<id\>** — Review with comments
116 **POST /api/reviews/\<id\>/comment** — Add review comment
117 **POST /api/reviews/\<id\>/approve** — Approve
118 **POST /api/reviews/\<id\>/request-changes** — Request changes
119 **POST /api/reviews/\<id\>/merge** — Merge approved review
120
121 ### Server-Sent Events
122
123 **GET /api/events** — Real-time event stream
124 ```
125 event: checkin
126 data: {"uuid": "abc123", "user": "dev", "comment": "Fix bug"}
127
128 event: claim
129 data: {"ticket": "def456", "agent": "claude-abc", "status": "claimed"}
130
131 event: workspace
132 data: {"name": "fix-auth", "branch": "workspace/fix-auth", "status": "merged"}
133 ```
134
135 ## MCP Server
136
137 The MCP (Model Context Protocol) server gives AI tools native access to FossilRepo.
138
139 ### Setup
140
141 ```bash
142 pip install fossilrepo
143 fossilrepo-mcp
144 ```
145
146 ### Claude Code Configuration
147
148 ```json
149 {
150 "mcpServers": {
151 "fossilrepo": {
152 "command": "fossilrepo-mcp"
153 }
154 }
155 }
156 ```
157
158 ### Available Tools
159
160 | Tool | Description |
161 |------|-------------|
162 | list_projects | List all projects |
163 | get_project | Project details with repo stats |
164 | browse_code | List files in a directory |
165 | read_file | Read file content |
166 | get_timeline | Recent checkins (optional branch filter) |
167 | get_checkin | Checkin detail with file changes |
168 | search_code | Search across checkins, tickets, wiki |
169 | list_tickets | List tickets (optional status filter) |
170 | get_ticket | Ticket detail with comments |
171 | create_ticket | Create a new ticket |
172 | update_ticket | Update ticket status, add comment |
173 | list_wiki_pages | List all wiki pages |
174 | get_wiki_page | Read wiki page content |
175 | list_branches | List all branches |
176 | get_file_blame | Blame annotations for a file |
177 | get_file_history | Commit history for a file |
178 | sql_query | Run read-only SQL against Fossil SQLite |
179
180 All tools accept a `slug` parameter to identify the project.
--- a/docs/architecture/api-reference.md
+++ b/docs/architecture/api-reference.md
@@ -0,0 +1,180 @@
1
+# API Reference
2
+
3
+FossilRepo provides a JSON API for programmatic access and an MCP server for AI tool integration.
4
+
5
+## Authentication
6
+
7
+All API endpoints accept authentication via:
8
+
9
+1. **Bearer token** (recommended for automation):
10
+ ```
11
+ Authorization: Bearer frp_abc123...
12
+ ```
13
+ Tokens can be project-scoped (API Token) or user-scoped (Personal Access Token).
14
+
15
+2. **Session cookie** (for browser-based testing):
16
+ Log in via the web UI, then call API endpoints in the same browser session.
17
+
18
+## JSON API Endpoints
19
+
20
+Base URL: `/projects/<slug>/fossil/api/`
21
+
22
+### Project
23
+
24
+**GET /api/project** — Project metadata
25
+```json
26
+{"name": "FossilRepo", "slug": "fossilrepo", "description": "...", "visibility": "public", "star_count": 5}
27
+```
28
+
29
+### Timeline
30
+
31
+**GET /api/timeline** — Recent checkins
32
+- `?page=1` — Page number
33
+- `?per_page=25` — Items per page
34
+- `?branch=trunk` — Filter by branch
35
+
36
+### Tickets
37
+
38
+**GET /api/tickets** — Ticket list
39
+- `?status=Open` — Filter by status
40
+- `?page=1&per_page=25` — Pagination
41
+
42
+**GET /api/tickets/\<uuid\>** — Single ticket with comments
43
+
44
+**GET /api/tickets/unclaimed** — Tickets available for agent claiming
45
+
46
+**POST /api/tickets/\<uuid\>/claim** — Claim ticket for exclusive work
47
+```json
48
+{"agent_id": "claude-session-abc"}
49
+```
50
+
51
+**POST /api/tickets/\<uuid\>/release** — Release a claim
52
+
53
+**POST /api/tickets/\<uuid\>/submit** — Submit completed work
54
+```json
55
+{"summary": "Fixed by...", "workspace": "agent-fix-123"}
56
+```
57
+
58
+### Wiki
59
+
60
+**GET /api/wiki** — Wiki page list
61
+**GET /api/wiki/\<name\>** — Page content (raw + rendered HTML)
62
+
63
+### Branches and Tags
64
+
65
+**GET /api/branches** — All branches with open/closed status
66
+**GET /api/tags** — All tags
67
+
68
+### Releases
69
+
70
+**GET /api/releases** — Release list with assets
71
+
72
+### Search
73
+
74
+**GET /api/search?q=term** — Search across checkins, tickets, wiki
75
+
76
+### CI Status
77
+
78
+**POST /api/status** — Report CI build status (Bearer token required)
79
+```json
80
+{"checkin": "abc123", "context": "ci/tests", "state": "success", "description": "All tests passed", "target_url": "https://ci.example.com/123"}
81
+```
82
+
83
+**GET /api/status/\<checkin_uuid\>/badge.svg** — SVG status badge
84
+
85
+### Batch API
86
+
87
+**POST /api/batch** — Execute up to 25 API calls in one request
88
+```json
89
+{"requests": [
90
+ {"method": "GET", "path": "/api/timeline", "params": {"per_page": 5}},
91
+ {"method": "GET", "path": "/api/tickets", "params": {"status": "Open"}},
92
+ {"method": "GET", "path": "/api/wiki/Home"}
93
+]}
94
+```
95
+
96
+### Agent Workspaces
97
+
98
+**GET /api/workspaces** — List active workspaces
99
+**POST /api/workspaces/create** — Create isolated workspace
100
+```json
101
+{"name": "fix-auth-bug", "agent_id": "claude-abc", "description": "Fixing auth issue"}
102
+```
103
+**GET /api/workspaces/\<name\>** — Workspace details
104
+**POST /api/workspaces/\<name\>/commit** — Commit changes
105
+**POST /api/workspaces/\<name\>/merge** — Merge back to trunk
106
+**DELETE /api/workspaces/\<name\>/abandon** — Abandon workspace
107
+
108
+### Code Reviews
109
+
110
+**GET /api/reviews** — List reviews (filterable by status)
111
+**POST /api/reviews/create** — Submit code for review
112
+```json
113
+{"title": "Fix null pointer", "description": "...", "diff": "--- a/...", "files_changed": ["src/auth.py"]}
114
+```
115
+**GET /api/reviews/\<id\>** — Review with comments
116
+**POST /api/reviews/\<id\>/comment** — Add review comment
117
+**POST /api/reviews/\<id\>/approve** — Approve
118
+**POST /api/reviews/\<id\>/request-changes** — Request changes
119
+**POST /api/reviews/\<id\>/merge** — Merge approved review
120
+
121
+### Server-Sent Events
122
+
123
+**GET /api/events** — Real-time event stream
124
+```
125
+event: checkin
126
+data: {"uuid": "abc123", "user": "dev", "comment": "Fix bug"}
127
+
128
+event: claim
129
+data: {"ticket": "def456", "agent": "claude-abc", "status": "claimed"}
130
+
131
+event: workspace
132
+data: {"name": "fix-auth", "branch": "workspace/fix-auth", "status": "merged"}
133
+```
134
+
135
+## MCP Server
136
+
137
+The MCP (Model Context Protocol) server gives AI tools native access to FossilRepo.
138
+
139
+### Setup
140
+
141
+```bash
142
+pip install fossilrepo
143
+fossilrepo-mcp
144
+```
145
+
146
+### Claude Code Configuration
147
+
148
+```json
149
+{
150
+ "mcpServers": {
151
+ "fossilrepo": {
152
+ "command": "fossilrepo-mcp"
153
+ }
154
+ }
155
+}
156
+```
157
+
158
+### Available Tools
159
+
160
+| Tool | Description |
161
+|------|-------------|
162
+| list_projects | List all projects |
163
+| get_project | Project details with repo stats |
164
+| browse_code | List files in a directory |
165
+| read_file | Read file content |
166
+| get_timeline | Recent checkins (optional branch filter) |
167
+| get_checkin | Checkin detail with file changes |
168
+| search_code | Search across checkins, tickets, wiki |
169
+| list_tickets | List tickets (optional status filter) |
170
+| get_ticket | Ticket detail with comments |
171
+| create_ticket | Create a new ticket |
172
+| update_ticket | Update ticket status, add comment |
173
+| list_wiki_pages | List all wiki pages |
174
+| get_wiki_page | Read wiki page content |
175
+| list_branches | List all branches |
176
+| get_file_blame | Blame annotations for a file |
177
+| get_file_history | Commit history for a file |
178
+| sql_query | Run read-only SQL against Fossil SQLite |
179
+
180
+All tools accept a `slug` parameter to identify the project.
--- a/docs/architecture/api-reference.md
+++ b/docs/architecture/api-reference.md
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/docs/architecture/api-reference.md
+++ b/docs/architecture/api-reference.md
@@ -0,0 +1,180 @@
1 # API Reference
2
3 FossilRepo provides a JSON API for programmatic access and an MCP server for AI tool integration.
4
5 ## Authentication
6
7 All API endpoints accept authentication via:
8
9 1. **Bearer token** (recommended for automation):
10 ```
11 Authorization: Bearer frp_abc123...
12 ```
13 Tokens can be project-scoped (API Token) or user-scoped (Personal Access Token).
14
15 2. **Session cookie** (for browser-based testing):
16 Log in via the web UI, then call API endpoints in the same browser session.
17
18 ## JSON API Endpoints
19
20 Base URL: `/projects/<slug>/fossil/api/`
21
22 ### Project
23
24 **GET /api/project** — Project metadata
25 ```json
26 {"name": "FossilRepo", "slug": "fossilrepo", "description": "...", "visibility": "public", "star_count": 5}
27 ```
28
29 ### Timeline
30
31 **GET /api/timeline** — Recent checkins
32 - `?page=1` — Page number
33 - `?per_page=25` — Items per page
34 - `?branch=trunk` — Filter by branch
35
36 ### Tickets
37
38 **GET /api/tickets** — Ticket list
39 - `?status=Open` — Filter by status
40 - `?page=1&per_page=25` — Pagination
41
42 **GET /api/tickets/\<uuid\>** — Single ticket with comments
43
44 **GET /api/tickets/unclaimed** — Tickets available for agent claiming
45
46 **POST /api/tickets/\<uuid\>/claim** — Claim ticket for exclusive work
47 ```json
48 {"agent_id": "claude-session-abc"}
49 ```
50
51 **POST /api/tickets/\<uuid\>/release** — Release a claim
52
53 **POST /api/tickets/\<uuid\>/submit** — Submit completed work
54 ```json
55 {"summary": "Fixed by...", "workspace": "agent-fix-123"}
56 ```
57
58 ### Wiki
59
60 **GET /api/wiki** — Wiki page list
61 **GET /api/wiki/\<name\>** — Page content (raw + rendered HTML)
62
63 ### Branches and Tags
64
65 **GET /api/branches** — All branches with open/closed status
66 **GET /api/tags** — All tags
67
68 ### Releases
69
70 **GET /api/releases** — Release list with assets
71
72 ### Search
73
74 **GET /api/search?q=term** — Search across checkins, tickets, wiki
75
76 ### CI Status
77
78 **POST /api/status** — Report CI build status (Bearer token required)
79 ```json
80 {"checkin": "abc123", "context": "ci/tests", "state": "success", "description": "All tests passed", "target_url": "https://ci.example.com/123"}
81 ```
82
83 **GET /api/status/\<checkin_uuid\>/badge.svg** — SVG status badge
84
85 ### Batch API
86
87 **POST /api/batch** — Execute up to 25 API calls in one request
88 ```json
89 {"requests": [
90 {"method": "GET", "path": "/api/timeline", "params": {"per_page": 5}},
91 {"method": "GET", "path": "/api/tickets", "params": {"status": "Open"}},
92 {"method": "GET", "path": "/api/wiki/Home"}
93 ]}
94 ```
95
96 ### Agent Workspaces
97
98 **GET /api/workspaces** — List active workspaces
99 **POST /api/workspaces/create** — Create isolated workspace
100 ```json
101 {"name": "fix-auth-bug", "agent_id": "claude-abc", "description": "Fixing auth issue"}
102 ```
103 **GET /api/workspaces/\<name\>** — Workspace details
104 **POST /api/workspaces/\<name\>/commit** — Commit changes
105 **POST /api/workspaces/\<name\>/merge** — Merge back to trunk
106 **DELETE /api/workspaces/\<name\>/abandon** — Abandon workspace
107
108 ### Code Reviews
109
110 **GET /api/reviews** — List reviews (filterable by status)
111 **POST /api/reviews/create** — Submit code for review
112 ```json
113 {"title": "Fix null pointer", "description": "...", "diff": "--- a/...", "files_changed": ["src/auth.py"]}
114 ```
115 **GET /api/reviews/\<id\>** — Review with comments
116 **POST /api/reviews/\<id\>/comment** — Add review comment
117 **POST /api/reviews/\<id\>/approve** — Approve
118 **POST /api/reviews/\<id\>/request-changes** — Request changes
119 **POST /api/reviews/\<id\>/merge** — Merge approved review
120
121 ### Server-Sent Events
122
123 **GET /api/events** — Real-time event stream
124 ```
125 event: checkin
126 data: {"uuid": "abc123", "user": "dev", "comment": "Fix bug"}
127
128 event: claim
129 data: {"ticket": "def456", "agent": "claude-abc", "status": "claimed"}
130
131 event: workspace
132 data: {"name": "fix-auth", "branch": "workspace/fix-auth", "status": "merged"}
133 ```
134
135 ## MCP Server
136
137 The MCP (Model Context Protocol) server gives AI tools native access to FossilRepo.
138
139 ### Setup
140
141 ```bash
142 pip install fossilrepo
143 fossilrepo-mcp
144 ```
145
146 ### Claude Code Configuration
147
148 ```json
149 {
150 "mcpServers": {
151 "fossilrepo": {
152 "command": "fossilrepo-mcp"
153 }
154 }
155 }
156 ```
157
158 ### Available Tools
159
160 | Tool | Description |
161 |------|-------------|
162 | list_projects | List all projects |
163 | get_project | Project details with repo stats |
164 | browse_code | List files in a directory |
165 | read_file | Read file content |
166 | get_timeline | Recent checkins (optional branch filter) |
167 | get_checkin | Checkin detail with file changes |
168 | search_code | Search across checkins, tickets, wiki |
169 | list_tickets | List tickets (optional status filter) |
170 | get_ticket | Ticket detail with comments |
171 | create_ticket | Create a new ticket |
172 | update_ticket | Update ticket status, add comment |
173 | list_wiki_pages | List all wiki pages |
174 | get_wiki_page | Read wiki page content |
175 | list_branches | List all branches |
176 | get_file_blame | Blame annotations for a file |
177 | get_file_history | Commit history for a file |
178 | sql_query | Run read-only SQL against Fossil SQLite |
179
180 All tools accept a `slug` parameter to identify the project.
--- docs/architecture/overview.md
+++ docs/architecture/overview.md
@@ -1,98 +1,141 @@
1
-# Architecture Overview
2
-
3
-Fossilrepo is a thin orchestration layer around Fossil SCM. Fossil does the heavy lifting -- fossilrepo handles provisioning, routing, backups, and the management UI.
4
-
5
-## System Diagram
6
-
7
-```mermaid
8
-graph TB
9
- subgraph Internet
10
- User[User / Browser]
11
- end
12
-
13
- subgraph Fossilrepo Server
14
- Caddy[Caddy<br/>SSL + Routing]
15
- Django[Django<br/>Management UI]
16
- Fossil[Fossil Server<br/>--repolist]
17
- Celery[Celery Workers]
18
- Redis[Redis]
19
- Postgres[(PostgreSQL)]
20
- Litestream[Litestream]
21
- end
22
-
23
- subgraph Storage
24
- Repos["/data/repos/<br/>*.fossil files"]
25
- S3[(S3 / MinIO)]
26
- end
27
-
28
- subgraph Mirrors
29
- GitHub[GitHub]
30
- GitLab[GitLab]
31
- end
32
-
33
- User --> Caddy
34
- Caddy -->|"app.domain.com"| Django
35
- Caddy -->|"repo.domain.com"| Fossil
36
- Django --> Postgres
37
- Django --> Redis
38
- Celery --> Redis
39
- Celery -->|sync bridge| GitHub
40
- Celery -->|sync bridge| GitLab
41
- Fossil --> Repos
42
- Litestream --> Repos
43
- Litestream --> S3
1
+# Architecture
2
+
3
+## Overview
4
+
5
+FossilRepo is a Django web application that wraps Fossil SCM repositories with a modern UI. It reads `.fossil` files directly as SQLite databases for speed, and uses the `fossil` CLI binary for write operations to maintain artifact integrity.
6
+
7
+```
8
+Browser (HTMX + Alpine.js + Tailwind CSS)
9
+ |
10
+ v
11
+Django 5 (views, ORM, permissions)
12
+ |
13
+ |-- FossilReader (direct SQLite reads, ?mode=ro)
14
+ |-- FossilCLI (subprocess for writes: commit, ticket, wiki, push/pull)
15
+ |-- fossil http (CGI proxy for clone/push/pull)
16
+ |
17
+ |-- PostgreSQL 16 (app data: users, orgs, teams, projects, settings)
18
+ |-- Redis 7 (Celery broker, cache)
19
+ |-- Celery (background: metadata sync, git mirror, webhooks, digest)
20
+ |
21
+ v
22
+.fossil files (SQLite: code + wiki + tickets + forum + technotes)
23
+ |
24
+ v
25
+Litestream --> S3 (continuous SQLite replication)
26
+```
27
+
28
+## Core Components
29
+
30
+### FossilReader
31
+
32
+Opens `.fossil` repository files directly as read-only SQLite databases. No network calls to a running Fossil server. Python's `sqlite3` module with `?mode=ro` URI.
33
+
34
+Handles:
35
+- Blob decompression (zlib with 4-byte size prefix)
36
+- Delta chain resolution (Fossil's delta-encoded artifacts)
37
+- Julian day timestamp conversion
38
+- Timeline queries, file tree at any checkin, ticket/wiki/forum reads
39
+- Commit activity aggregation, contributor stats, search
40
+
41
+### FossilCLI
42
+
43
+Thin subprocess wrapper around the `fossil` binary. Used for all write operations:
44
+- Repository init, clone, push, pull, sync
45
+- Ticket create/change, wiki create/commit
46
+- Technote creation, blame, Pikchr rendering
47
+- Git export for mirror sync
48
+- Tarball and zip archive generation
49
+- Unversioned file management
50
+- Artifact shunning
51
+
52
+All calls set `USER=fossilrepo` in the environment and call `ensure_default_user()` to prevent "cannot figure out who you are" errors.
53
+
54
+### HTTP Sync Proxy
55
+
56
+The `fossil_xfer` view proxies Fossil's wire protocol through Django. Clients clone/push/pull via:
57
+
58
+```
59
+fossil clone http://your-server/projects/<slug>/fossil/xfer repo.fossil
60
+```
61
+
62
+Django handles authentication and access control. Public repos allow anonymous pull (no `--localauth`). Authenticated users with write access get full push via `--localauth`. Branch protection rules are enforced at this layer.
63
+
64
+### SSH Sync
65
+
66
+An `sshd` instance runs on port 2222 with a restricted `fossil-shell` forced command. Users upload SSH public keys via their profile. The `authorized_keys` file is regenerated from the database on key add/remove.
67
+
68
+```
69
+fossil clone ssh://fossil@host:2222/<slug> repo.fossil
4470
```
4571
46
-## Components
72
+## Data Architecture
73
+
74
+### Two Databases
75
+
76
+1. **PostgreSQL** — application state: users, organizations, teams, projects, releases, webhooks, API tokens, workspace claims, code reviews, notification preferences
77
+2. **Fossil .fossil files** — repository data: code history, tickets, wiki, forum, technotes, unversioned files
78
+
79
+### Model Base Classes
80
+
81
+- `Tracking` (abstract) — `version`, `created_at/by`, `updated_at/by`, `deleted_at/by`, `history` (django-simple-history)
82
+- `BaseCoreModel(Tracking)` — adds `guid` (UUID4), `name`, `slug` (auto-generated), `description`
83
+- Soft deletes only: `obj.soft_delete(user=request.user)`, never `.delete()`
84
+- `ActiveManager` on `objects` excludes soft-deleted; `all_objects` includes them
85
+
86
+### Permission Model
87
+
88
+Two layers:
89
+1. **Org-level roles** (Admin/Manager/Developer/Viewer) — Django Groups with permission bundles, assignable per user
90
+2. **Project-level RBAC** (read/write/admin) — per team, via ProjectTeam model
91
+
92
+Project visibility:
93
+- **Public** — anyone can read (including anonymous)
94
+- **Internal** — authenticated users can read
95
+- **Private** — team members only
96
+
97
+### Encryption
98
+
99
+SSH keys and OAuth tokens encrypted at rest using Fernet (AES-128-CBC + HMAC-SHA256), keyed from Django's `SECRET_KEY`. Implemented as `EncryptedTextField` in `core/fields.py`.
100
+
101
+## Infrastructure
102
+
103
+### Docker (Omnibus)
104
+
105
+Single multi-stage Dockerfile:
106
+1. Stage 1: compile Fossil 2.24 from source (Debian bookworm)
107
+2. Stage 2: Python 3.12 runtime with Fossil binary, sshd, gosu
108
+
109
+Entrypoint starts sshd as root, drops to unprivileged `app` user for gunicorn.
47110
48
-### Fossil Server
111
+### Celery Tasks
49112
50
-A single `fossil server --repolist /data/repos/` process serves all repositories. Each `.fossil` file is a self-contained SQLite database with VCS history, issues, wiki, and forum.
51
-
52
-Adding a new repo is just `fossil init /data/repos/name.fossil` -- no restart needed.
113
+| Task | Schedule | Purpose |
114
+|------|----------|---------|
115
+| sync_metadata | Every 5 min | Update repo stats (size, checkin count) |
116
+| check_upstream | Every 15 min | Check for new upstream artifacts |
117
+| dispatch_notifications | Every 5 min | Send pending email notifications |
118
+| send_digest (daily) | Every 24h | Daily notification digest |
119
+| send_digest (weekly) | Every 7d | Weekly notification digest |
120
+| dispatch_webhook | On event | Deliver webhook with retry |
121
+| run_git_sync | On schedule | Git mirror export |
53122
54123
### Caddy
55124
56
-Handles SSL termination and subdomain routing:
57
-
58
-- `your-domain.com` routes to the Django management UI
59
-- `reponame.your-domain.com` routes directly to Fossil's web UI
60
-
61
-Caddy automatically provisions and renews Let's Encrypt certificates.
62
-
63
-### Django Management Layer
64
-
65
-Provides the administrative interface:
66
-
67
-- Repository lifecycle (create, configure, archive)
68
-- User and organization management
69
-- Dashboard and analytics
70
-- Sync bridge configuration
71
-
72
-Django uses HTMX for interactive UI without a JavaScript framework.
125
+SSL termination and subdomain routing. Each repo can get its own subdomain.
73126
74127
### Litestream
75128
76
-Continuously replicates every `.fossil` SQLite file to S3-compatible storage. Provides:
77
-
78
-- **Continuous backup** -- WAL frames replicated in near-real-time
79
-- **Point-in-time recovery** -- restore to any moment, not just snapshots
80
-- **Zero-config per repo** -- new `.fossil` files are picked up automatically
81
-
82
-### Celery Workers
83
-
84
-Handle background tasks:
85
-
86
-- Sync bridge execution (Fossil to Git mirroring)
87
-- Scheduled sync jobs
88
-- Upstream pull operations
89
-
90
-## Data Flow
91
-
92
-1. **User pushes to Fossil** -- standard `fossil push` or `fossil sync`
93
-2. **Fossil writes to `.fossil` file** -- SQLite transactions
94
-3. **Litestream replicates** -- WAL frames streamed to S3
95
-4. **Sync bridge runs** -- Celery task mirrors changes to Git remotes
96
-5. **Django reflects state** -- reads from Fossil SQLite for dashboards
97
-
98
-Fossil is always the source of truth. Everything else is derived.
129
+Continuous SQLite-to-S3 replication for `.fossil` files. Point-in-time recovery.
130
+
131
+## Django Apps
132
+
133
+| App | Purpose |
134
+|-----|---------|
135
+| `core` | Base models, permissions, pagination, sanitization, encryption |
136
+| `accounts` | Login/logout, SSH keys, user profile, personal access tokens |
137
+| `organization` | Org settings, teams, members, roles |
138
+| `projects` | Projects, project groups, project stars, team assignment |
139
+| `pages` | FossilRepo KB (knowledge base articles) |
140
+| `fossil` | Everything Fossil: reader, CLI, views, sync, webhooks, releases, CI, workspaces, reviews |
141
+| `mcp_server` | MCP server for AI tool integration |
99142
100143
ADDED docs/features.md
101144
ADDED docs/getting-started/administration.md
--- docs/architecture/overview.md
+++ docs/architecture/overview.md
@@ -1,98 +1,141 @@
1 # Architecture Overview
2
3 Fossilrepo is a thin orchestration layer around Fossil SCM. Fossil does the heavy lifting -- fossilrepo handles provisioning, routing, backups, and the management UI.
4
5 ## System Diagram
6
7 ```mermaid
8 graph TB
9 subgraph Internet
10 User[User / Browser]
11 end
12
13 subgraph Fossilrepo Server
14 Caddy[Caddy<br/>SSL + Routing]
15 Django[Django<br/>Management UI]
16 Fossil[Fossil Server<br/>--repolist]
17 Celery[Celery Workers]
18 Redis[Redis]
19 Postgres[(PostgreSQL)]
20 Litestream[Litestream]
21 end
22
23 subgraph Storage
24 Repos["/data/repos/<br/>*.fossil files"]
25 S3[(S3 / MinIO)]
26 end
27
28 subgraph Mirrors
29 GitHub[GitHub]
30 GitLab[GitLab]
31 end
32
33 User --> Caddy
34 Caddy -->|"app.domain.com"| Django
35 Caddy -->|"repo.domain.com"| Fossil
36 Django --> Postgres
37 Django --> Redis
38 Celery --> Redis
39 Celery -->|sync bridge| GitHub
40 Celery -->|sync bridge| GitLab
41 Fossil --> Repos
42 Litestream --> Repos
43 Litestream --> S3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44 ```
45
46 ## Components
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
48 ### Fossil Server
49
50 A single `fossil server --repolist /data/repos/` process serves all repositories. Each `.fossil` file is a self-contained SQLite database with VCS history, issues, wiki, and forum.
51
52 Adding a new repo is just `fossil init /data/repos/name.fossil` -- no restart needed.
 
 
 
 
 
 
53
54 ### Caddy
55
56 Handles SSL termination and subdomain routing:
57
58 - `your-domain.com` routes to the Django management UI
59 - `reponame.your-domain.com` routes directly to Fossil's web UI
60
61 Caddy automatically provisions and renews Let's Encrypt certificates.
62
63 ### Django Management Layer
64
65 Provides the administrative interface:
66
67 - Repository lifecycle (create, configure, archive)
68 - User and organization management
69 - Dashboard and analytics
70 - Sync bridge configuration
71
72 Django uses HTMX for interactive UI without a JavaScript framework.
73
74 ### Litestream
75
76 Continuously replicates every `.fossil` SQLite file to S3-compatible storage. Provides:
77
78 - **Continuous backup** -- WAL frames replicated in near-real-time
79 - **Point-in-time recovery** -- restore to any moment, not just snapshots
80 - **Zero-config per repo** -- new `.fossil` files are picked up automatically
81
82 ### Celery Workers
83
84 Handle background tasks:
85
86 - Sync bridge execution (Fossil to Git mirroring)
87 - Scheduled sync jobs
88 - Upstream pull operations
89
90 ## Data Flow
91
92 1. **User pushes to Fossil** -- standard `fossil push` or `fossil sync`
93 2. **Fossil writes to `.fossil` file** -- SQLite transactions
94 3. **Litestream replicates** -- WAL frames streamed to S3
95 4. **Sync bridge runs** -- Celery task mirrors changes to Git remotes
96 5. **Django reflects state** -- reads from Fossil SQLite for dashboards
97
98 Fossil is always the source of truth. Everything else is derived.
99
100 DDED docs/features.md
101 DDED docs/getting-started/administration.md
--- docs/architecture/overview.md
+++ docs/architecture/overview.md
@@ -1,98 +1,141 @@
1 # Architecture
2
3 ## Overview
4
5 FossilRepo is a Django web application that wraps Fossil SCM repositories with a modern UI. It reads `.fossil` files directly as SQLite databases for speed, and uses the `fossil` CLI binary for write operations to maintain artifact integrity.
6
7 ```
8 Browser (HTMX + Alpine.js + Tailwind CSS)
9 |
10 v
11 Django 5 (views, ORM, permissions)
12 |
13 |-- FossilReader (direct SQLite reads, ?mode=ro)
14 |-- FossilCLI (subprocess for writes: commit, ticket, wiki, push/pull)
15 |-- fossil http (CGI proxy for clone/push/pull)
16 |
17 |-- PostgreSQL 16 (app data: users, orgs, teams, projects, settings)
18 |-- Redis 7 (Celery broker, cache)
19 |-- Celery (background: metadata sync, git mirror, webhooks, digest)
20 |
21 v
22 .fossil files (SQLite: code + wiki + tickets + forum + technotes)
23 |
24 v
25 Litestream --> S3 (continuous SQLite replication)
26 ```
27
28 ## Core Components
29
30 ### FossilReader
31
32 Opens `.fossil` repository files directly as read-only SQLite databases. No network calls to a running Fossil server. Python's `sqlite3` module with `?mode=ro` URI.
33
34 Handles:
35 - Blob decompression (zlib with 4-byte size prefix)
36 - Delta chain resolution (Fossil's delta-encoded artifacts)
37 - Julian day timestamp conversion
38 - Timeline queries, file tree at any checkin, ticket/wiki/forum reads
39 - Commit activity aggregation, contributor stats, search
40
41 ### FossilCLI
42
43 Thin subprocess wrapper around the `fossil` binary. Used for all write operations:
44 - Repository init, clone, push, pull, sync
45 - Ticket create/change, wiki create/commit
46 - Technote creation, blame, Pikchr rendering
47 - Git export for mirror sync
48 - Tarball and zip archive generation
49 - Unversioned file management
50 - Artifact shunning
51
52 All calls set `USER=fossilrepo` in the environment and call `ensure_default_user()` to prevent "cannot figure out who you are" errors.
53
54 ### HTTP Sync Proxy
55
56 The `fossil_xfer` view proxies Fossil's wire protocol through Django. Clients clone/push/pull via:
57
58 ```
59 fossil clone http://your-server/projects/<slug>/fossil/xfer repo.fossil
60 ```
61
62 Django handles authentication and access control. Public repos allow anonymous pull (no `--localauth`). Authenticated users with write access get full push via `--localauth`. Branch protection rules are enforced at this layer.
63
64 ### SSH Sync
65
66 An `sshd` instance runs on port 2222 with a restricted `fossil-shell` forced command. Users upload SSH public keys via their profile. The `authorized_keys` file is regenerated from the database on key add/remove.
67
68 ```
69 fossil clone ssh://fossil@host:2222/<slug> repo.fossil
70 ```
71
72 ## Data Architecture
73
74 ### Two Databases
75
76 1. **PostgreSQL** — application state: users, organizations, teams, projects, releases, webhooks, API tokens, workspace claims, code reviews, notification preferences
77 2. **Fossil .fossil files** — repository data: code history, tickets, wiki, forum, technotes, unversioned files
78
79 ### Model Base Classes
80
81 - `Tracking` (abstract) — `version`, `created_at/by`, `updated_at/by`, `deleted_at/by`, `history` (django-simple-history)
82 - `BaseCoreModel(Tracking)` — adds `guid` (UUID4), `name`, `slug` (auto-generated), `description`
83 - Soft deletes only: `obj.soft_delete(user=request.user)`, never `.delete()`
84 - `ActiveManager` on `objects` excludes soft-deleted; `all_objects` includes them
85
86 ### Permission Model
87
88 Two layers:
89 1. **Org-level roles** (Admin/Manager/Developer/Viewer) — Django Groups with permission bundles, assignable per user
90 2. **Project-level RBAC** (read/write/admin) — per team, via ProjectTeam model
91
92 Project visibility:
93 - **Public** — anyone can read (including anonymous)
94 - **Internal** — authenticated users can read
95 - **Private** — team members only
96
97 ### Encryption
98
99 SSH keys and OAuth tokens encrypted at rest using Fernet (AES-128-CBC + HMAC-SHA256), keyed from Django's `SECRET_KEY`. Implemented as `EncryptedTextField` in `core/fields.py`.
100
101 ## Infrastructure
102
103 ### Docker (Omnibus)
104
105 Single multi-stage Dockerfile:
106 1. Stage 1: compile Fossil 2.24 from source (Debian bookworm)
107 2. Stage 2: Python 3.12 runtime with Fossil binary, sshd, gosu
108
109 Entrypoint starts sshd as root, drops to unprivileged `app` user for gunicorn.
110
111 ### Celery Tasks
112
113 | Task | Schedule | Purpose |
114 |------|----------|---------|
115 | sync_metadata | Every 5 min | Update repo stats (size, checkin count) |
116 | check_upstream | Every 15 min | Check for new upstream artifacts |
117 | dispatch_notifications | Every 5 min | Send pending email notifications |
118 | send_digest (daily) | Every 24h | Daily notification digest |
119 | send_digest (weekly) | Every 7d | Weekly notification digest |
120 | dispatch_webhook | On event | Deliver webhook with retry |
121 | run_git_sync | On schedule | Git mirror export |
122
123 ### Caddy
124
125 SSL termination and subdomain routing. Each repo can get its own subdomain.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
127 ### Litestream
128
129 Continuous SQLite-to-S3 replication for `.fossil` files. Point-in-time recovery.
130
131 ## Django Apps
132
133 | App | Purpose |
134 |-----|---------|
135 | `core` | Base models, permissions, pagination, sanitization, encryption |
136 | `accounts` | Login/logout, SSH keys, user profile, personal access tokens |
137 | `organization` | Org settings, teams, members, roles |
138 | `projects` | Projects, project groups, project stars, team assignment |
139 | `pages` | FossilRepo KB (knowledge base articles) |
140 | `fossil` | Everything Fossil: reader, CLI, views, sync, webhooks, releases, CI, workspaces, reviews |
141 | `mcp_server` | MCP server for AI tool integration |
 
 
 
 
 
 
 
 
 
 
142
143 DDED docs/features.md
144 DDED docs/getting-started/administration.md
--- a/docs/features.md
+++ b/docs/features.md
@@ -0,0 +1,205 @@
1
+# Features
2
+
3
+## Code Browser
4
+- Directory navigation with breadcrumbs and file size display
5
+- Syntax-highlighted source view with line numbers and permalinks
6
+- Copy-link popover on line number click
7
+- Blame with age-based coloring (newest = red, oldest = gray)
8
+- File history showing all checkins that touched a file
9
+- Raw file download
10
+- Rendered preview for Markdown, HTML, and other markup
11
+- README auto-rendering at directory level
12
+
13
+## Timeline
14
+- DAG graph with fork/merge connectors and color-coded branches (8-color palette)
15
+- Merge commit diamonds, leaf indicators (open circles)
16
+- Date headers grouping commits by day
17
+- Keyboard navigation (j/k to move, Enter to open)
18
+- HTMX infinite scroll for seamless loading
19
+- Event type filtering (checkins, wiki, tickets)
20
+- RSS feed
21
+
22
+## Diffs
23
+- Unified and side-by-side view (toggle with localStorage preference)
24
+- Syntax highlighting via highlight.js (auto-detected from file extension)
25
+- Color-coded additions (green) and deletions (red)
26
+- Line-level permalinks
27
+- Compare any two checkins
28
+- Fossil delta decoding for accurate diff computation
29
+
30
+## Tickets
31
+- Full CRUD: create, edit, close/reopen, add comments
32
+- Filter by status, type, priority, severity
33
+- Pagination with configurable per-page (25/50/100)
34
+- Live search via HTMX
35
+- CSV export
36
+- Custom field definitions (text, textarea, select, checkbox, date, URL)
37
+- Custom SQL ticket reports with injection prevention
38
+- Per-page count selector
39
+
40
+## Wiki
41
+- Markdown + Fossil wiki markup + raw HTML rendering
42
+- Pikchr diagram rendering (via fossil CLI)
43
+- Create and edit pages
44
+- Right-sidebar table of contents
45
+- Internal link rewriting (Fossil URLs mapped to app URLs)
46
+- Footnotes, tables, fenced code blocks
47
+
48
+## Forum
49
+- Threaded discussions (Fossil-native + Django-backed posts)
50
+- Create new threads with markdown body
51
+- Post replies with threading
52
+- Merged view showing both Fossil and Django posts
53
+
54
+## Releases
55
+- Versioned releases with tag names and markdown changelogs
56
+- Source code archives: tar.gz and zip (via fossil tarball/zip)
57
+- File attachments with download counts
58
+- Draft and prerelease support
59
+- CRUD for authorized users
60
+
61
+## Technotes
62
+- Create and edit developer journal entries
63
+- Markdown body with preview
64
+- Timestamped, shown in timeline
65
+
66
+## Unversioned Files
67
+- Browse Fossil's unversioned content (equivalent to Git LFS)
68
+- File list with size and date
69
+- Download individual files
70
+- Admin upload via fossil uv CLI
71
+
72
+## Branches, Tags, Technotes
73
+- List all branches with open/closed status
74
+- List all tags
75
+- Searchable and paginated
76
+
77
+## Search
78
+- Full-text search across checkins, tickets, and wiki pages
79
+- Global search shortcut (/ key)
80
+- Per-project scoped search
81
+
82
+## Sync
83
+- Pull from upstream Fossil remotes
84
+- Push to downstream Fossil remotes
85
+- Bidirectional sync
86
+- Git mirror to GitHub/GitLab via OAuth or SSH key auth
87
+- Multiple mirrors per repo, each with own schedule and direction
88
+- Configurable sync modes: on-change, scheduled (cron), both, disabled
89
+- Clone/push/pull over HTTP (fossil http CGI proxy)
90
+- Clone/push/pull over SSH (port 2222, forced command)
91
+
92
+## Webhooks
93
+- Outbound HTTP webhooks on checkin, ticket, wiki, release events
94
+- HMAC-SHA256 signed payloads
95
+- Exponential backoff retry (3 attempts)
96
+- Delivery log with response status and timing
97
+- Per-project webhook configuration
98
+
99
+## CI Status Checks
100
+- External API for CI systems to POST build status per checkin
101
+- Bearer token authentication
102
+- SVG badge endpoint for embedding in READMEs
103
+- Status display on checkin detail page (green/red/yellow icons)
104
+
105
+## Releases
106
+- Create/edit/delete versioned releases
107
+- Link to Fossil checkin
108
+- Markdown changelog body
109
+- Source code download (tar.gz, zip)
110
+- File attachments with download tracking
111
+- Draft and prerelease flags
112
+
113
+## Organization Management
114
+- Single-org model with settings, website, description
115
+- Member management: create, edit, deactivate, change password
116
+- Team management: create, assign members
117
+- Project groups: organize related repos under a group header
118
+- Project-level team roles: read, write, admin
119
+
120
+## Roles and Permissions
121
+- Predefined roles: Admin, Manager, Developer, Viewer
122
+- Custom role creation with permission picker (grouped by app)
123
+- Role assignment on user create/edit
124
+- Permissions synced to Django Groups automatically
125
+- Two-layer model: org-level roles + project-level RBAC
126
+
127
+## User Profiles
128
+- Personal profile page: name, email, @handle, bio, location, website
129
+- SSH key management with encrypted storage
130
+- Personal access tokens (frp_ prefix, hash-only storage)
131
+- Notification preferences (immediate/daily/weekly/off + event toggles)
132
+- Change password
133
+
134
+## Project Features
135
+- Project starring with counts
136
+- Explore/discover page for public projects (sort by stars/recent/name)
137
+- Project groups for organizing related repos
138
+- Public/internal/private visibility
139
+- Anonymous access for public repos (all read views)
140
+
141
+## API Tokens and Deploy Keys
142
+- Project-scoped API tokens with SHA-256 hashed storage
143
+- Token shown once on creation, never stored in plaintext
144
+- Configurable permissions and expiry
145
+- Last-used tracking
146
+
147
+## Branch Protection
148
+- Per-branch protection rules with glob pattern matching
149
+- Restrict push to admins only
150
+- Required CI status check contexts
151
+- Enforced on HTTP sync, CLI push/sync, and SSH push
152
+
153
+## Artifact Shunning
154
+- Admin UI for permanently removing artifacts
155
+- Type-to-confirm safety (must enter first 8 chars of UUID)
156
+- Calls fossil shun CLI
157
+- Irreversible with clear warning
158
+
159
+## SQLite Explorer
160
+- Visual schema map with category-colored table cards
161
+- SVG relationship graph showing Fossil's internal table connections
162
+- HTMX-powered table browser with column definitions and paginated data
163
+- Custom SQL query runner (SELECT only, validated against injection)
164
+- Admin-only access
165
+
166
+## Audit Log
167
+- Unified view of all model changes via django-simple-history
168
+- Filter by model type (Project, Organization, Team, Repository)
169
+- Shows user, action (Created/Changed/Deleted), timestamp
170
+- Superuser/org-admin access
171
+
172
+## Email Notifications
173
+- HTML email templates (dark themed, inline CSS for email clients)
174
+- Immediate delivery per event
175
+- Daily/weekly digest mode
176
+- Per-user event type toggles (checkins, tickets, wiki, releases, forum)
177
+- Unsubscribe links
178
+
179
+## Agentic Development Platform
180
+- MCP server with 17 tools for AI assistant integration
181
+- JSON API: 10+ read endpoints with Bearer token auth
182
+- Batch API: execute up to 25 API calls in one request
183
+- Agent workspaces: isolated Fossil branches per agent
184
+- Atomic ticket claiming for multi-agent coordination
185
+- Server-Sent Events for real-time notifications
186
+- Code review API: submit diffs, comment, approve, merge
187
+
188
+## UI/UX
189
+- Dark/light theme with system preference detection
190
+- Collapsible sidebar with project tree navigation
191
+- Keyboard shortcuts (j/k, Enter, /, ?)
192
+- Consistent pagination (25/50/100 per-page selector) across all lists
193
+- HTMX live search with 300ms debounce
194
+- Mobile responsive (slide-out drawer)
195
+- Custom branded error pages (403, 404, 500)
196
+- Public nav for anonymous users (logo, Explore, Sign in)
197
+
198
+## Infrastructure
199
+- Omnibus Docker image (Fossil compiled from source)
200
+- Multi-arch builds (amd64 + arm64)
201
+- Caddy for SSL termination and subdomain routing
202
+- Litestream for continuous SQLite-to-S3 replication
203
+- Supply chain attestations (SLSA provenance + SBOM)
204
+- Non-root container execution (gosu privilege dropping)
205
+- Celery Beat for scheduled tasks
--- a/docs/features.md
+++ b/docs/features.md
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/docs/features.md
+++ b/docs/features.md
@@ -0,0 +1,205 @@
1 # Features
2
3 ## Code Browser
4 - Directory navigation with breadcrumbs and file size display
5 - Syntax-highlighted source view with line numbers and permalinks
6 - Copy-link popover on line number click
7 - Blame with age-based coloring (newest = red, oldest = gray)
8 - File history showing all checkins that touched a file
9 - Raw file download
10 - Rendered preview for Markdown, HTML, and other markup
11 - README auto-rendering at directory level
12
13 ## Timeline
14 - DAG graph with fork/merge connectors and color-coded branches (8-color palette)
15 - Merge commit diamonds, leaf indicators (open circles)
16 - Date headers grouping commits by day
17 - Keyboard navigation (j/k to move, Enter to open)
18 - HTMX infinite scroll for seamless loading
19 - Event type filtering (checkins, wiki, tickets)
20 - RSS feed
21
22 ## Diffs
23 - Unified and side-by-side view (toggle with localStorage preference)
24 - Syntax highlighting via highlight.js (auto-detected from file extension)
25 - Color-coded additions (green) and deletions (red)
26 - Line-level permalinks
27 - Compare any two checkins
28 - Fossil delta decoding for accurate diff computation
29
30 ## Tickets
31 - Full CRUD: create, edit, close/reopen, add comments
32 - Filter by status, type, priority, severity
33 - Pagination with configurable per-page (25/50/100)
34 - Live search via HTMX
35 - CSV export
36 - Custom field definitions (text, textarea, select, checkbox, date, URL)
37 - Custom SQL ticket reports with injection prevention
38 - Per-page count selector
39
40 ## Wiki
41 - Markdown + Fossil wiki markup + raw HTML rendering
42 - Pikchr diagram rendering (via fossil CLI)
43 - Create and edit pages
44 - Right-sidebar table of contents
45 - Internal link rewriting (Fossil URLs mapped to app URLs)
46 - Footnotes, tables, fenced code blocks
47
48 ## Forum
49 - Threaded discussions (Fossil-native + Django-backed posts)
50 - Create new threads with markdown body
51 - Post replies with threading
52 - Merged view showing both Fossil and Django posts
53
54 ## Releases
55 - Versioned releases with tag names and markdown changelogs
56 - Source code archives: tar.gz and zip (via fossil tarball/zip)
57 - File attachments with download counts
58 - Draft and prerelease support
59 - CRUD for authorized users
60
61 ## Technotes
62 - Create and edit developer journal entries
63 - Markdown body with preview
64 - Timestamped, shown in timeline
65
66 ## Unversioned Files
67 - Browse Fossil's unversioned content (equivalent to Git LFS)
68 - File list with size and date
69 - Download individual files
70 - Admin upload via fossil uv CLI
71
72 ## Branches, Tags, Technotes
73 - List all branches with open/closed status
74 - List all tags
75 - Searchable and paginated
76
77 ## Search
78 - Full-text search across checkins, tickets, and wiki pages
79 - Global search shortcut (/ key)
80 - Per-project scoped search
81
82 ## Sync
83 - Pull from upstream Fossil remotes
84 - Push to downstream Fossil remotes
85 - Bidirectional sync
86 - Git mirror to GitHub/GitLab via OAuth or SSH key auth
87 - Multiple mirrors per repo, each with own schedule and direction
88 - Configurable sync modes: on-change, scheduled (cron), both, disabled
89 - Clone/push/pull over HTTP (fossil http CGI proxy)
90 - Clone/push/pull over SSH (port 2222, forced command)
91
92 ## Webhooks
93 - Outbound HTTP webhooks on checkin, ticket, wiki, release events
94 - HMAC-SHA256 signed payloads
95 - Exponential backoff retry (3 attempts)
96 - Delivery log with response status and timing
97 - Per-project webhook configuration
98
99 ## CI Status Checks
100 - External API for CI systems to POST build status per checkin
101 - Bearer token authentication
102 - SVG badge endpoint for embedding in READMEs
103 - Status display on checkin detail page (green/red/yellow icons)
104
105 ## Releases
106 - Create/edit/delete versioned releases
107 - Link to Fossil checkin
108 - Markdown changelog body
109 - Source code download (tar.gz, zip)
110 - File attachments with download tracking
111 - Draft and prerelease flags
112
113 ## Organization Management
114 - Single-org model with settings, website, description
115 - Member management: create, edit, deactivate, change password
116 - Team management: create, assign members
117 - Project groups: organize related repos under a group header
118 - Project-level team roles: read, write, admin
119
120 ## Roles and Permissions
121 - Predefined roles: Admin, Manager, Developer, Viewer
122 - Custom role creation with permission picker (grouped by app)
123 - Role assignment on user create/edit
124 - Permissions synced to Django Groups automatically
125 - Two-layer model: org-level roles + project-level RBAC
126
127 ## User Profiles
128 - Personal profile page: name, email, @handle, bio, location, website
129 - SSH key management with encrypted storage
130 - Personal access tokens (frp_ prefix, hash-only storage)
131 - Notification preferences (immediate/daily/weekly/off + event toggles)
132 - Change password
133
134 ## Project Features
135 - Project starring with counts
136 - Explore/discover page for public projects (sort by stars/recent/name)
137 - Project groups for organizing related repos
138 - Public/internal/private visibility
139 - Anonymous access for public repos (all read views)
140
141 ## API Tokens and Deploy Keys
142 - Project-scoped API tokens with SHA-256 hashed storage
143 - Token shown once on creation, never stored in plaintext
144 - Configurable permissions and expiry
145 - Last-used tracking
146
147 ## Branch Protection
148 - Per-branch protection rules with glob pattern matching
149 - Restrict push to admins only
150 - Required CI status check contexts
151 - Enforced on HTTP sync, CLI push/sync, and SSH push
152
153 ## Artifact Shunning
154 - Admin UI for permanently removing artifacts
155 - Type-to-confirm safety (must enter first 8 chars of UUID)
156 - Calls fossil shun CLI
157 - Irreversible with clear warning
158
159 ## SQLite Explorer
160 - Visual schema map with category-colored table cards
161 - SVG relationship graph showing Fossil's internal table connections
162 - HTMX-powered table browser with column definitions and paginated data
163 - Custom SQL query runner (SELECT only, validated against injection)
164 - Admin-only access
165
166 ## Audit Log
167 - Unified view of all model changes via django-simple-history
168 - Filter by model type (Project, Organization, Team, Repository)
169 - Shows user, action (Created/Changed/Deleted), timestamp
170 - Superuser/org-admin access
171
172 ## Email Notifications
173 - HTML email templates (dark themed, inline CSS for email clients)
174 - Immediate delivery per event
175 - Daily/weekly digest mode
176 - Per-user event type toggles (checkins, tickets, wiki, releases, forum)
177 - Unsubscribe links
178
179 ## Agentic Development Platform
180 - MCP server with 17 tools for AI assistant integration
181 - JSON API: 10+ read endpoints with Bearer token auth
182 - Batch API: execute up to 25 API calls in one request
183 - Agent workspaces: isolated Fossil branches per agent
184 - Atomic ticket claiming for multi-agent coordination
185 - Server-Sent Events for real-time notifications
186 - Code review API: submit diffs, comment, approve, merge
187
188 ## UI/UX
189 - Dark/light theme with system preference detection
190 - Collapsible sidebar with project tree navigation
191 - Keyboard shortcuts (j/k, Enter, /, ?)
192 - Consistent pagination (25/50/100 per-page selector) across all lists
193 - HTMX live search with 300ms debounce
194 - Mobile responsive (slide-out drawer)
195 - Custom branded error pages (403, 404, 500)
196 - Public nav for anonymous users (logo, Explore, Sign in)
197
198 ## Infrastructure
199 - Omnibus Docker image (Fossil compiled from source)
200 - Multi-arch builds (amd64 + arm64)
201 - Caddy for SSL termination and subdomain routing
202 - Litestream for continuous SQLite-to-S3 replication
203 - Supply chain attestations (SLSA provenance + SBOM)
204 - Non-root container execution (gosu privilege dropping)
205 - Celery Beat for scheduled tasks
--- a/docs/getting-started/administration.md
+++ b/docs/getting-started/administration.md
@@ -0,0 +1,131 @@
1
+# Administration
2
+
3
+## User Management
4
+
5
+Navigate to **Admin > Members** in the sidebar.
6
+
7
+### Creating Users
8
+1. Click "Create User"
9
+2. Fill in username, email, name, password
10
+3. Optionally assign an org role
11
+4. User is automatically added as an organization member
12
+
13
+### Editing Users
14
+Click a username to view their profile, then "Edit" to change:
15
+- Name, email
16
+- Active/inactive status
17
+- Staff status (access to Super Admin)
18
+- Org role assignment
19
+
20
+### Changing Passwords
21
+From the user detail page, click "Change Password". Admins can change any user's password. Users can change their own password from their profile page.
22
+
23
+### Deactivating Users
24
+Edit the user and uncheck "Active". This prevents login without deleting the account. The user's history and contributions are preserved.
25
+
26
+## Roles
27
+
28
+Navigate to **Admin > Roles** in the sidebar.
29
+
30
+### Predefined Roles
31
+
32
+| Role | Access Level |
33
+|------|-------------|
34
+| Admin | Full access to everything |
35
+| Manager | Manage projects, teams, members, pages |
36
+| Developer | Contribute: view projects, create tickets |
37
+| Viewer | Read-only access to all content |
38
+
39
+### Custom Roles
40
+Click "Create Role" to define a custom role with a specific permission set. The permission picker groups permissions by app (Organization, Projects, Pages, Fossil).
41
+
42
+### Initializing Roles
43
+If no roles exist, click "Initialize Roles" to create the four predefined roles. This runs the `seed_roles` management command.
44
+
45
+### How Roles Work
46
+Each role maps to a Django Group with the same permissions. When a user is assigned a role, their previous role group is removed and the new one added. Permissions are synced automatically.
47
+
48
+## Teams
49
+
50
+Navigate to **Admin > Teams** in the sidebar.
51
+
52
+Teams are groups of users that can be assigned to projects with specific access levels.
53
+
54
+### Creating Teams
55
+1. Click "New Team"
56
+2. Enter name and description
57
+3. Add members from the user list
58
+
59
+### Assigning Teams to Projects
60
+1. Go to the project overview
61
+2. Click the project name > Teams section
62
+3. Click "Add Team"
63
+4. Select team and role (read/write/admin)
64
+
65
+## Project Groups
66
+
67
+Navigate to **Admin > Groups** in the sidebar.
68
+
69
+Groups organize related projects together in the sidebar. For example, "Fossil SCM" group might contain the source code repo, forum repo, and docs repo.
70
+
71
+### Creating Groups
72
+1. Click "Create Group"
73
+2. Enter name and description
74
+3. Assign projects to the group via the project edit form
75
+
76
+## Organization Settings
77
+
78
+Navigate to **Admin > Settings** in the sidebar.
79
+
80
+Configure the organization name, website, and description. This appears in the site header and various admin pages.
81
+
82
+## Audit Log
83
+
84
+Navigate to **Admin > Audit Log** in the sidebar.
85
+
86
+Shows all model changes across the application, powered by django-simple-history. Filter by model type to see changes to specific entities.
87
+
88
+## Super Admin
89
+
90
+Navigate to **Admin > Super Admin** in the sidebar.
91
+
92
+This is Django's built-in admin interface. Use it for:
93
+- Direct database access to any model
94
+- Constance runtime settings
95
+- Celery task results and beat schedule
96
+- Advanced permission management
97
+- Data import/export
98
+
99
+Most day-to-day operations should be done through the main UI, not Super Admin.
100
+
101
+## Project Settings
102
+
103
+Each project has its own settings tab (visible to project admins):
104
+
105
+### Repository Info
106
+- Filename, file size, project code, checkin/ticket/wiki counts
107
+
108
+### Remote URL
109
+- Configure upstream Fossil remote for pull/push/sync
110
+
111
+### Clone URLs
112
+- HTTP clone URL for users
113
+- SSH clone URL
114
+
115
+### Tokens
116
+- Project-scoped API tokens for CI/CD integration
117
+
118
+### Branch Protection
119
+- Per-branch rules: restrict push, require CI status checks
120
+
121
+### Webhooks
122
+- Outbound webhooks on repository events
123
+
124
+## Notification Settings
125
+
126
+Users configure their own notification preferences at **/auth/notifications/**:
127
+
128
+- **Delivery mode**: Immediate, Daily Digest, Weekly Digest, Off
129
+- **Event types**: Checkins, Tickets, Wiki, Releases, Forum
130
+
131
+Admins can view user preferences via Super Admin.
--- a/docs/getting-started/administration.md
+++ b/docs/getting-started/administration.md
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/docs/getting-started/administration.md
+++ b/docs/getting-started/administration.md
@@ -0,0 +1,131 @@
1 # Administration
2
3 ## User Management
4
5 Navigate to **Admin > Members** in the sidebar.
6
7 ### Creating Users
8 1. Click "Create User"
9 2. Fill in username, email, name, password
10 3. Optionally assign an org role
11 4. User is automatically added as an organization member
12
13 ### Editing Users
14 Click a username to view their profile, then "Edit" to change:
15 - Name, email
16 - Active/inactive status
17 - Staff status (access to Super Admin)
18 - Org role assignment
19
20 ### Changing Passwords
21 From the user detail page, click "Change Password". Admins can change any user's password. Users can change their own password from their profile page.
22
23 ### Deactivating Users
24 Edit the user and uncheck "Active". This prevents login without deleting the account. The user's history and contributions are preserved.
25
26 ## Roles
27
28 Navigate to **Admin > Roles** in the sidebar.
29
30 ### Predefined Roles
31
32 | Role | Access Level |
33 |------|-------------|
34 | Admin | Full access to everything |
35 | Manager | Manage projects, teams, members, pages |
36 | Developer | Contribute: view projects, create tickets |
37 | Viewer | Read-only access to all content |
38
39 ### Custom Roles
40 Click "Create Role" to define a custom role with a specific permission set. The permission picker groups permissions by app (Organization, Projects, Pages, Fossil).
41
42 ### Initializing Roles
43 If no roles exist, click "Initialize Roles" to create the four predefined roles. This runs the `seed_roles` management command.
44
45 ### How Roles Work
46 Each role maps to a Django Group with the same permissions. When a user is assigned a role, their previous role group is removed and the new one added. Permissions are synced automatically.
47
48 ## Teams
49
50 Navigate to **Admin > Teams** in the sidebar.
51
52 Teams are groups of users that can be assigned to projects with specific access levels.
53
54 ### Creating Teams
55 1. Click "New Team"
56 2. Enter name and description
57 3. Add members from the user list
58
59 ### Assigning Teams to Projects
60 1. Go to the project overview
61 2. Click the project name > Teams section
62 3. Click "Add Team"
63 4. Select team and role (read/write/admin)
64
65 ## Project Groups
66
67 Navigate to **Admin > Groups** in the sidebar.
68
69 Groups organize related projects together in the sidebar. For example, "Fossil SCM" group might contain the source code repo, forum repo, and docs repo.
70
71 ### Creating Groups
72 1. Click "Create Group"
73 2. Enter name and description
74 3. Assign projects to the group via the project edit form
75
76 ## Organization Settings
77
78 Navigate to **Admin > Settings** in the sidebar.
79
80 Configure the organization name, website, and description. This appears in the site header and various admin pages.
81
82 ## Audit Log
83
84 Navigate to **Admin > Audit Log** in the sidebar.
85
86 Shows all model changes across the application, powered by django-simple-history. Filter by model type to see changes to specific entities.
87
88 ## Super Admin
89
90 Navigate to **Admin > Super Admin** in the sidebar.
91
92 This is Django's built-in admin interface. Use it for:
93 - Direct database access to any model
94 - Constance runtime settings
95 - Celery task results and beat schedule
96 - Advanced permission management
97 - Data import/export
98
99 Most day-to-day operations should be done through the main UI, not Super Admin.
100
101 ## Project Settings
102
103 Each project has its own settings tab (visible to project admins):
104
105 ### Repository Info
106 - Filename, file size, project code, checkin/ticket/wiki counts
107
108 ### Remote URL
109 - Configure upstream Fossil remote for pull/push/sync
110
111 ### Clone URLs
112 - HTTP clone URL for users
113 - SSH clone URL
114
115 ### Tokens
116 - Project-scoped API tokens for CI/CD integration
117
118 ### Branch Protection
119 - Per-branch rules: restrict push, require CI status checks
120
121 ### Webhooks
122 - Outbound webhooks on repository events
123
124 ## Notification Settings
125
126 Users configure their own notification preferences at **/auth/notifications/**:
127
128 - **Delivery mode**: Immediate, Daily Digest, Weekly Digest, Off
129 - **Event types**: Checkins, Tickets, Wiki, Releases, Forum
130
131 Admins can view user preferences via Super Admin.
--- docs/getting-started/installation.md
+++ docs/getting-started/installation.md
@@ -1,115 +1,113 @@
1
-# Installation
1
+# Setup Guide
22
3
-## Clone the Repository
3
+## Quick Start (Docker)
44
55
```bash
66
git clone https://github.com/ConflictHQ/fossilrepo.git
77
cd fossilrepo
8
-```
9
-
10
-## Environment Configuration
11
-
12
-Copy the example environment file and configure it:
13
-
14
-```bash
15
-cp .env.example .env
16
-```
17
-
18
-Edit `.env` with your settings:
19
-
20
-```ini
21
-# Django
22
-SECRET_KEY=your-secret-key-here
23
-DEBUG=True
24
-ALLOWED_HOSTS=localhost,127.0.0.1
25
-
26
-# Database
27
-POSTGRES_DB=fossilrepo
28
-POSTGRES_USER=fossilrepo
29
-POSTGRES_PASSWORD=your-db-password
30
-
31
-# Redis
32
-REDIS_URL=redis://redis:6379/0
33
-
34
-# Fossil
35
-FOSSIL_REPO_DIR=/data/repos
36
-FOSSIL_BASE_URL=https://your-domain.com
37
-```
38
-
39
-## Start the Stack
40
-
41
-### Development
42
-
43
-```bash
44
-# Build and start all services
45
-make build
46
-
47
-# Run database migrations
48
-make migrate
49
-
50
-# Create an admin user
51
-make superuser
52
-
53
-# Load sample data (optional)
54
-make seed
55
-```
56
-
57
-The development stack includes:
58
-
59
-- Django dev server on `http://localhost:8000`
60
-- PostgreSQL 16
61
-- Redis
62
-- Celery worker + beat
63
-- Mailpit on `http://localhost:8025`
64
-
65
-### Production
66
-
67
-For production, you'll also configure Caddy and Litestream:
68
-
69
-```bash
70
-# Copy production configs
71
-cp docker/Caddyfile.example docker/Caddyfile
72
-cp docker/litestream.yml.example docker/litestream.yml
73
-
74
-# Edit with your domain and S3 credentials
75
-# Then start with the production compose file
76
-docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
77
-```
78
-
79
-## Verify Installation
80
-
81
-```bash
82
-# Check all services are running
83
-docker compose ps
84
-
85
-# Hit the health endpoint
86
-curl http://localhost:8000/health/
87
-
88
-# Open the dashboard
89
-open http://localhost:8000
90
-```
91
-
92
-!!! success "You should see"
93
- The fossilrepo dashboard with navigation, login page, and (after seeding) sample repositories.
94
-
95
-## Common Issues
96
-
97
-??? question "Port 8000 already in use"
98
- Change the Django port mapping in `docker-compose.yml`:
99
- ```yaml
100
- ports:
101
- - "8001:8000"
102
- ```
103
-
104
-??? question "Database connection refused"
105
- Ensure PostgreSQL has started before Django:
106
- ```bash
107
- docker compose logs postgres
108
- ```
109
- The Django container waits for Postgres to be ready, but network issues on some Docker Desktop versions can cause timeouts. Restart with `make down && make up`.
110
-
111
-??? question "Permission denied on /data/repos"
112
- The Fossil repo directory needs to be writable by the container user:
113
- ```bash
114
- sudo chown -R 1000:1000 /data/repos
115
- ```
8
+docker compose up -d --build
9
+docker compose exec backend python manage.py migrate
10
+docker compose exec backend python manage.py seed
11
+docker compose exec backend python manage.py seed_roles
12
+```
13
+
14
+Visit http://localhost:8000. Login: `admin` / `admin`.
15
+
16
+## Default Users
17
+
18
+| Username | Password | Role |
19
+|----------|----------|------|
20
+| admin | admin | Superuser |
21
+| viewer | viewer | View-only |
22
+| role-admin | role-admin | Admin role |
23
+| role-manager | role-manager | Manager role |
24
+| role-developer | role-developer | Developer role |
25
+| role-viewer | role-viewer | Viewer role |
26
+
27
+## Configuration
28
+
29
+### Environment Variables
30
+
31
+Copy `.env.example` to `.env` and customize. Key variables:
32
+
33
+| Variable | Default | Description |
34
+|----------|---------|-------------|
35
+| DJANGO_SECRET_KEY | change-me | **Required in production** |
36
+| DJANGO_DEBUG | false | Enable debug mode |
37
+| DJANGO_ALLOWED_HOSTS | localhost | Comma-separated hostnames |
38
+| POSTGRES_DB | fossilrepo | Database name |
39
+| POSTGRES_USER | dbadmin | Database user |
40
+| POSTGRES_PASSWORD | Password123 | Database password |
41
+| REDIS_URL | redis://localhost:6379/1 | Redis connection |
42
+| EMAIL_HOST | localhost | SMTP server |
43
+| CORS_ALLOWED_ORIGINS | http://localhost:8000 | CORS origins |
44
+| SENTRY_DSN | (empty) | Sentry error tracking |
45
+
46
+### Runtime Settings (Constance)
47
+
48
+Configurable via Django admin at `/admin/constance/config/`:
49
+
50
+| Setting | Default | Description |
51
+|---------|---------|-------------|
52
+| SITE_NAME | Fossilrepo | Display name |
53
+| FOSSIL_DATA_DIR | /data/repos | Where .fossil files live |
54
+| FOSSIL_BINARY_PATH | fossil | Path to fossil binary |
55
+| FOSSIL_STORE_IN_DB | false | Store snapshots via Django file storage |
56
+| FOSSIL_S3_TRACKING | false | Track S3 replication |
57
+| GIT_SYNC_MODE | disabled | Default sync mode |
58
+| GIT_SYNC_SCHEDULE | */15 * * * * | Default cron for git sync |
59
+
60
+### OAuth (GitHub/GitLab)
61
+
62
+For Git mirror sync via OAuth:
63
+1. Create an OAuth App on GitHub/GitLab
64
+2. Set Client ID and Secret in Constance (Django admin)
65
+3. Callback URLs are handled automatically
66
+
67
+## Adding Repositories
68
+
69
+### Create Empty
70
+
71
+1. Go to Projects > + New
72
+2. Fill in name and description
73
+3. Select "Create empty repository"
74
+4. Done — empty .fossil file created
75
+
76
+### Clone from Fossil URL
77
+
78
+1. Go to Projects > + New
79
+2. Select "Clone from Fossil URL"
80
+3. Enter the URL (e.g., `https://fossil-scm.org/home`)
81
+4. FossilRepo clones the repo and links it
82
+
83
+### Clone Fossil SCM (Example)
84
+
85
+```bash
86
+# Clone the official Fossil SCM repo
87
+docker compose exec backend fossil clone https://fossil-scm.org/home /data/repos/fossil-scm.fossil
88
+```
89
+
90
+Then create a Project in the UI and link it to the file.
91
+
92
+## Production Deployment
93
+
94
+See `.env.production.example` for production settings. Key steps:
95
+
96
+1. Set a strong `DJANGO_SECRET_KEY`
97
+2. Set `DJANGO_DEBUG=false`
98
+3. Configure `DJANGO_ALLOWED_HOSTS` to your domain
99
+4. Use a proper database password
100
+5. Configure email (SES, SMTP)
101
+6. Set up HTTPS via Caddy or reverse proxy
102
+7. Configure S3 for Litestream backups (optional)
103
+
104
+## Ports
105
+
106
+| Port | Service |
107
+|------|---------|
108
+| 8000 | Django (HTTP) |
109
+| 2222 | SSH (Fossil sync) |
110
+| 5432 | PostgreSQL |
111
+| 6379 | Redis |
112
+| 1025 | Mailpit SMTP (dev) |
113
+| 8025 | Mailpit UI (dev) |
116114
--- docs/getting-started/installation.md
+++ docs/getting-started/installation.md
@@ -1,115 +1,113 @@
1 # Installation
2
3 ## Clone the Repository
4
5 ```bash
6 git clone https://github.com/ConflictHQ/fossilrepo.git
7 cd fossilrepo
8 ```
9
10 ## Environment Configuration
11
12 Copy the example environment file and configure it:
13
14 ```bash
15 cp .env.example .env
16 ```
17
18 Edit `.env` with your settings:
19
20 ```ini
21 # Django
22 SECRET_KEY=your-secret-key-here
23 DEBUG=True
24 ALLOWED_HOSTS=localhost,127.0.0.1
25
26 # Database
27 POSTGRES_DB=fossilrepo
28 POSTGRES_USER=fossilrepo
29 POSTGRES_PASSWORD=your-db-password
30
31 # Redis
32 REDIS_URL=redis://redis:6379/0
33
34 # Fossil
35 FOSSIL_REPO_DIR=/data/repos
36 FOSSIL_BASE_URL=https://your-domain.com
37 ```
38
39 ## Start the Stack
40
41 ### Development
42
43 ```bash
44 # Build and start all services
45 make build
46
47 # Run database migrations
48 make migrate
49
50 # Create an admin user
51 make superuser
52
53 # Load sample data (optional)
54 make seed
55 ```
56
57 The development stack includes:
58
59 - Django dev server on `http://localhost:8000`
60 - PostgreSQL 16
61 - Redis
62 - Celery worker + beat
63 - Mailpit on `http://localhost:8025`
64
65 ### Production
66
67 For production, you'll also configure Caddy and Litestream:
68
69 ```bash
70 # Copy production configs
71 cp docker/Caddyfile.example docker/Caddyfile
72 cp docker/litestream.yml.example docker/litestream.yml
73
74 # Edit with your domain and S3 credentials
75 # Then start with the production compose file
76 docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
77 ```
78
79 ## Verify Installation
80
81 ```bash
82 # Check all services are running
83 docker compose ps
84
85 # Hit the health endpoint
86 curl http://localhost:8000/health/
87
88 # Open the dashboard
89 open http://localhost:8000
90 ```
91
92 !!! success "You should see"
93 The fossilrepo dashboard with navigation, login page, and (after seeding) sample repositories.
94
95 ## Common Issues
96
97 ??? question "Port 8000 already in use"
98 Change the Django port mapping in `docker-compose.yml`:
99 ```yaml
100 ports:
101 - "8001:8000"
102 ```
103
104 ??? question "Database connection refused"
105 Ensure PostgreSQL has started before Django:
106 ```bash
107 docker compose logs postgres
108 ```
109 The Django container waits for Postgres to be ready, but network issues on some Docker Desktop versions can cause timeouts. Restart with `make down && make up`.
110
111 ??? question "Permission denied on /data/repos"
112 The Fossil repo directory needs to be writable by the container user:
113 ```bash
114 sudo chown -R 1000:1000 /data/repos
115 ```
116
--- docs/getting-started/installation.md
+++ docs/getting-started/installation.md
@@ -1,115 +1,113 @@
1 # Setup Guide
2
3 ## Quick Start (Docker)
4
5 ```bash
6 git clone https://github.com/ConflictHQ/fossilrepo.git
7 cd fossilrepo
8 docker compose up -d --build
9 docker compose exec backend python manage.py migrate
10 docker compose exec backend python manage.py seed
11 docker compose exec backend python manage.py seed_roles
12 ```
13
14 Visit http://localhost:8000. Login: `admin` / `admin`.
15
16 ## Default Users
17
18 | Username | Password | Role |
19 |----------|----------|------|
20 | admin | admin | Superuser |
21 | viewer | viewer | View-only |
22 | role-admin | role-admin | Admin role |
23 | role-manager | role-manager | Manager role |
24 | role-developer | role-developer | Developer role |
25 | role-viewer | role-viewer | Viewer role |
26
27 ## Configuration
28
29 ### Environment Variables
30
31 Copy `.env.example` to `.env` and customize. Key variables:
32
33 | Variable | Default | Description |
34 |----------|---------|-------------|
35 | DJANGO_SECRET_KEY | change-me | **Required in production** |
36 | DJANGO_DEBUG | false | Enable debug mode |
37 | DJANGO_ALLOWED_HOSTS | localhost | Comma-separated hostnames |
38 | POSTGRES_DB | fossilrepo | Database name |
39 | POSTGRES_USER | dbadmin | Database user |
40 | POSTGRES_PASSWORD | Password123 | Database password |
41 | REDIS_URL | redis://localhost:6379/1 | Redis connection |
42 | EMAIL_HOST | localhost | SMTP server |
43 | CORS_ALLOWED_ORIGINS | http://localhost:8000 | CORS origins |
44 | SENTRY_DSN | (empty) | Sentry error tracking |
45
46 ### Runtime Settings (Constance)
47
48 Configurable via Django admin at `/admin/constance/config/`:
49
50 | Setting | Default | Description |
51 |---------|---------|-------------|
52 | SITE_NAME | Fossilrepo | Display name |
53 | FOSSIL_DATA_DIR | /data/repos | Where .fossil files live |
54 | FOSSIL_BINARY_PATH | fossil | Path to fossil binary |
55 | FOSSIL_STORE_IN_DB | false | Store snapshots via Django file storage |
56 | FOSSIL_S3_TRACKING | false | Track S3 replication |
57 | GIT_SYNC_MODE | disabled | Default sync mode |
58 | GIT_SYNC_SCHEDULE | */15 * * * * | Default cron for git sync |
59
60 ### OAuth (GitHub/GitLab)
61
62 For Git mirror sync via OAuth:
63 1. Create an OAuth App on GitHub/GitLab
64 2. Set Client ID and Secret in Constance (Django admin)
65 3. Callback URLs are handled automatically
66
67 ## Adding Repositories
68
69 ### Create Empty
70
71 1. Go to Projects > + New
72 2. Fill in name and description
73 3. Select "Create empty repository"
74 4. Done — empty .fossil file created
75
76 ### Clone from Fossil URL
77
78 1. Go to Projects > + New
79 2. Select "Clone from Fossil URL"
80 3. Enter the URL (e.g., `https://fossil-scm.org/home`)
81 4. FossilRepo clones the repo and links it
82
83 ### Clone Fossil SCM (Example)
84
85 ```bash
86 # Clone the official Fossil SCM repo
87 docker compose exec backend fossil clone https://fossil-scm.org/home /data/repos/fossil-scm.fossil
88 ```
89
90 Then create a Project in the UI and link it to the file.
91
92 ## Production Deployment
93
94 See `.env.production.example` for production settings. Key steps:
95
96 1. Set a strong `DJANGO_SECRET_KEY`
97 2. Set `DJANGO_DEBUG=false`
98 3. Configure `DJANGO_ALLOWED_HOSTS` to your domain
99 4. Use a proper database password
100 5. Configure email (SES, SMTP)
101 6. Set up HTTPS via Caddy or reverse proxy
102 7. Configure S3 for Litestream backups (optional)
103
104 ## Ports
105
106 | Port | Service |
107 |------|---------|
108 | 8000 | Django (HTTP) |
109 | 2222 | SSH (Fossil sync) |
110 | 5432 | PostgreSQL |
111 | 6379 | Redis |
112 | 1025 | Mailpit SMTP (dev) |
113 | 8025 | Mailpit UI (dev) |
 
 
114
--- fossil/views.py
+++ fossil/views.py
@@ -761,10 +761,13 @@
761761
762762
with reader:
763763
pages = reader.get_wiki_pages()
764764
home_page = reader.get_wiki_page("Home")
765765
766
+ # Sort: Home first, then alphabetical
767
+ pages = sorted(pages, key=lambda p: ("" if p.name == "Home" else "~" + p.name.lower()))
768
+
766769
search = request.GET.get("search", "").strip()
767770
if search:
768771
pages = [p for p in pages if search.lower() in p.name.lower()]
769772
770773
per_page = get_per_page(request)
@@ -801,10 +804,13 @@
801804
all_pages = reader.get_wiki_pages()
802805
803806
if not page:
804807
raise Http404(f"Wiki page not found: {page_name}")
805808
809
+ # Sort: Home first, then alphabetical
810
+ all_pages = sorted(all_pages, key=lambda p: ("" if p.name == "Home" else "~" + p.name.lower()))
811
+
806812
content_html = mark_safe(sanitize_html(_render_fossil_content(page.content, project_slug=slug)))
807813
808814
return render(
809815
request,
810816
"fossil/wiki_page.html",
811817
--- fossil/views.py
+++ fossil/views.py
@@ -761,10 +761,13 @@
761
762 with reader:
763 pages = reader.get_wiki_pages()
764 home_page = reader.get_wiki_page("Home")
765
 
 
 
766 search = request.GET.get("search", "").strip()
767 if search:
768 pages = [p for p in pages if search.lower() in p.name.lower()]
769
770 per_page = get_per_page(request)
@@ -801,10 +804,13 @@
801 all_pages = reader.get_wiki_pages()
802
803 if not page:
804 raise Http404(f"Wiki page not found: {page_name}")
805
 
 
 
806 content_html = mark_safe(sanitize_html(_render_fossil_content(page.content, project_slug=slug)))
807
808 return render(
809 request,
810 "fossil/wiki_page.html",
811
--- fossil/views.py
+++ fossil/views.py
@@ -761,10 +761,13 @@
761
762 with reader:
763 pages = reader.get_wiki_pages()
764 home_page = reader.get_wiki_page("Home")
765
766 # Sort: Home first, then alphabetical
767 pages = sorted(pages, key=lambda p: ("" if p.name == "Home" else "~" + p.name.lower()))
768
769 search = request.GET.get("search", "").strip()
770 if search:
771 pages = [p for p in pages if search.lower() in p.name.lower()]
772
773 per_page = get_per_page(request)
@@ -801,10 +804,13 @@
804 all_pages = reader.get_wiki_pages()
805
806 if not page:
807 raise Http404(f"Wiki page not found: {page_name}")
808
809 # Sort: Home first, then alphabetical
810 all_pages = sorted(all_pages, key=lambda p: ("" if p.name == "Home" else "~" + p.name.lower()))
811
812 content_html = mark_safe(sanitize_html(_render_fossil_content(page.content, project_slug=slug)))
813
814 return render(
815 request,
816 "fossil/wiki_page.html",
817
+5
--- mkdocs.yml
+++ mkdocs.yml
@@ -67,18 +67,23 @@
6767
- toc:
6868
permalink: true
6969
7070
nav:
7171
- Home: index.md
72
+ - Features: features.md
7273
- Getting Started:
7374
- Prerequisites: getting-started/prerequisites.md
7475
- Installation: getting-started/installation.md
7576
- Configuration: getting-started/configuration.md
7677
- First Repository: getting-started/first-repo.md
78
+ - Administration: getting-started/administration.md
7779
- Architecture:
7880
- Overview: architecture/overview.md
7981
- Sync Bridge: architecture/sync-bridge.md
82
+ - API:
83
+ - Reference: api/reference.md
84
+ - Agentic Development: api/agentic-development.md
8085
8186
extra:
8287
social:
8388
- icon: fontawesome/brands/github
8489
link: https://github.com/ConflictHQ/fossilrepo
8590
8691
ADDED scripts/sync_to_fossil.sh
--- mkdocs.yml
+++ mkdocs.yml
@@ -67,18 +67,23 @@
67 - toc:
68 permalink: true
69
70 nav:
71 - Home: index.md
 
72 - Getting Started:
73 - Prerequisites: getting-started/prerequisites.md
74 - Installation: getting-started/installation.md
75 - Configuration: getting-started/configuration.md
76 - First Repository: getting-started/first-repo.md
 
77 - Architecture:
78 - Overview: architecture/overview.md
79 - Sync Bridge: architecture/sync-bridge.md
 
 
 
80
81 extra:
82 social:
83 - icon: fontawesome/brands/github
84 link: https://github.com/ConflictHQ/fossilrepo
85
86 DDED scripts/sync_to_fossil.sh
--- mkdocs.yml
+++ mkdocs.yml
@@ -67,18 +67,23 @@
67 - toc:
68 permalink: true
69
70 nav:
71 - Home: index.md
72 - Features: features.md
73 - Getting Started:
74 - Prerequisites: getting-started/prerequisites.md
75 - Installation: getting-started/installation.md
76 - Configuration: getting-started/configuration.md
77 - First Repository: getting-started/first-repo.md
78 - Administration: getting-started/administration.md
79 - Architecture:
80 - Overview: architecture/overview.md
81 - Sync Bridge: architecture/sync-bridge.md
82 - API:
83 - Reference: api/reference.md
84 - Agentic Development: api/agentic-development.md
85
86 extra:
87 social:
88 - icon: fontawesome/brands/github
89 link: https://github.com/ConflictHQ/fossilrepo
90
91 DDED scripts/sync_to_fossil.sh
--- a/scripts/sync_to_fossil.sh
+++ b/scripts/sync_to_fossil.sh
@@ -0,0 +1,73 @@
1
+#!/bin/bash
2
+# sync_to_fossil.sh — Commit current code into the existing .fossil repo.
3
+#
4
+# Opens a temporary checkout, rsyncs the working tree in, commits changes.
5
+# NEVER replaces or reimports the .fossil file — preserves all tickets,
6
+# wiki, forum, and other Fossil-native artifacts.
7
+#
8
+# Usage: ./scripts/sync_to_fossil.sh ["commit message"]
9
+# Run from inside the container or via:
10
+# docker compose exec backend bash scripts/sync_to_fossil.sh "message"
11
+
12
+set -euo pipefail
13
+
14
+REPO="/data/repos/fossilrepo.fossil"
15
+WORKDIR="/tmp/fossil-checkout-$$"
16
+MESSAGE="${1:-Sync from working tree}"
17
+
18
+export USER="${USER:-ragelink}"
19
+
20
+if [ ! -f "$REPO" ]; then
21
+ echo "Error: $REPO not found" >&2
22
+ exit 1
23
+fi
24
+
25
+echo "=== Committing to Fossil ==="
26
+
27
+# Create temp checkout
28
+rm -rf "$WORKDIR"
29
+mkdir -p "$WORKDIR"
30
+cd "$WORKDIR"
31
+
32
+fossil open "$REPO" --workdir "$WORKDIR" 2>/dev/null
33
+fossil update trunk 2>/dev/null || true
34
+
35
+# Sync code from /app — use tar to copy with exclusions (rsync not available)
36
+cd /app
37
+tar cf - \
38
+ --exclude='.git' \
39
+ --exclude='__pycache__' \
40
+ --exclude='*.pyc' \
41
+ --exclude='.ruff_cache' \
42
+ --exclude='node_modules' \
43
+ --exclude='assets' \
44
+ --exclude='.env' \
45
+ --exclude='repos' \
46
+ --exclude='.fslckout' \
47
+ --exclude='_FOSSIL_' \
48
+ --exclude='*.fossil' \
49
+ --exclude='.claude' \
50
+ . | (cd "$WORKDIR" && tar xf -)
51
+cd "$WORKDIR"
52
+
53
+# Register new/deleted files
54
+fossil addremove 2>/dev/null || true
55
+
56
+# Commit if there are changes
57
+CHANGES=$(fossil changes 2>/dev/null | wc -l | tr -d ' ')
58
+if [ "$CHANGES" -gt 0 ]; then
59
+ fossil commit -m "$MESSAGE" --no-warnings 2>&1 | tail -3
60
+ echo "Committed $CHANGES changed files."
61
+else
62
+ echo "No changes to commit."
63
+fi
64
+
65
+# Cleanup
66
+fossil close --force 2>/dev/null || true
67
+cd /
68
+rm -rf "$WORKDIR"
69
+
70
+echo "=== Status ==="
71
+echo "Checkins: $(fossil sql -R "$REPO" "SELECT count(*) FROM event WHERE type='ci';" | tr -d "' ")"
72
+echo "Wiki: $(fossil wiki list -R "$REPO" | wc -l) pages"
73
+echo "Tickets: $(fossil sql -R "$REPO" "SELECT count(*) FROM ticket;" | tr -d "' ")"
--- a/scripts/sync_to_fossil.sh
+++ b/scripts/sync_to_fossil.sh
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/scripts/sync_to_fossil.sh
+++ b/scripts/sync_to_fossil.sh
@@ -0,0 +1,73 @@
1 #!/bin/bash
2 # sync_to_fossil.sh — Commit current code into the existing .fossil repo.
3 #
4 # Opens a temporary checkout, rsyncs the working tree in, commits changes.
5 # NEVER replaces or reimports the .fossil file — preserves all tickets,
6 # wiki, forum, and other Fossil-native artifacts.
7 #
8 # Usage: ./scripts/sync_to_fossil.sh ["commit message"]
9 # Run from inside the container or via:
10 # docker compose exec backend bash scripts/sync_to_fossil.sh "message"
11
12 set -euo pipefail
13
14 REPO="/data/repos/fossilrepo.fossil"
15 WORKDIR="/tmp/fossil-checkout-$$"
16 MESSAGE="${1:-Sync from working tree}"
17
18 export USER="${USER:-ragelink}"
19
20 if [ ! -f "$REPO" ]; then
21 echo "Error: $REPO not found" >&2
22 exit 1
23 fi
24
25 echo "=== Committing to Fossil ==="
26
27 # Create temp checkout
28 rm -rf "$WORKDIR"
29 mkdir -p "$WORKDIR"
30 cd "$WORKDIR"
31
32 fossil open "$REPO" --workdir "$WORKDIR" 2>/dev/null
33 fossil update trunk 2>/dev/null || true
34
35 # Sync code from /app — use tar to copy with exclusions (rsync not available)
36 cd /app
37 tar cf - \
38 --exclude='.git' \
39 --exclude='__pycache__' \
40 --exclude='*.pyc' \
41 --exclude='.ruff_cache' \
42 --exclude='node_modules' \
43 --exclude='assets' \
44 --exclude='.env' \
45 --exclude='repos' \
46 --exclude='.fslckout' \
47 --exclude='_FOSSIL_' \
48 --exclude='*.fossil' \
49 --exclude='.claude' \
50 . | (cd "$WORKDIR" && tar xf -)
51 cd "$WORKDIR"
52
53 # Register new/deleted files
54 fossil addremove 2>/dev/null || true
55
56 # Commit if there are changes
57 CHANGES=$(fossil changes 2>/dev/null | wc -l | tr -d ' ')
58 if [ "$CHANGES" -gt 0 ]; then
59 fossil commit -m "$MESSAGE" --no-warnings 2>&1 | tail -3
60 echo "Committed $CHANGES changed files."
61 else
62 echo "No changes to commit."
63 fi
64
65 # Cleanup
66 fossil close --force 2>/dev/null || true
67 cd /
68 rm -rf "$WORKDIR"
69
70 echo "=== Status ==="
71 echo "Checkins: $(fossil sql -R "$REPO" "SELECT count(*) FROM event WHERE type='ci';" | tr -d "' ")"
72 echo "Wiki: $(fossil wiki list -R "$REPO" | wc -l) pages"
73 echo "Tickets: $(fossil sql -R "$REPO" "SELECT count(*) FROM ticket;" | tr -d "' ")"
--- templates/dashboard.html
+++ templates/dashboard.html
@@ -106,11 +106,11 @@
106106
<p class="mt-1 text-xs text-gray-500">Organize members into teams</p>
107107
</a>
108108
{% endif %}
109109
{% if perms.pages.view_page %}
110110
<a href="{% url 'pages:list' %}" class="block rounded-lg bg-gray-800 border border-gray-700 p-4 hover:border-brand transition-colors">
111
- <h3 class="text-sm font-semibold text-gray-100">FossilRepo KB</h3>
111
+ <h3 class="text-sm font-semibold text-gray-100">FossilRepo Docs</h3>
112112
<p class="mt-1 text-xs text-gray-500">Guides, runbooks, documentation</p>
113113
</a>
114114
{% endif %}
115115
{% if perms.organization.view_organization %}
116116
<a href="{% url 'organization:settings' %}" class="block rounded-lg bg-gray-800 border border-gray-700 p-4 hover:border-brand transition-colors">
117117
--- templates/dashboard.html
+++ templates/dashboard.html
@@ -106,11 +106,11 @@
106 <p class="mt-1 text-xs text-gray-500">Organize members into teams</p>
107 </a>
108 {% endif %}
109 {% if perms.pages.view_page %}
110 <a href="{% url 'pages:list' %}" class="block rounded-lg bg-gray-800 border border-gray-700 p-4 hover:border-brand transition-colors">
111 <h3 class="text-sm font-semibold text-gray-100">FossilRepo KB</h3>
112 <p class="mt-1 text-xs text-gray-500">Guides, runbooks, documentation</p>
113 </a>
114 {% endif %}
115 {% if perms.organization.view_organization %}
116 <a href="{% url 'organization:settings' %}" class="block rounded-lg bg-gray-800 border border-gray-700 p-4 hover:border-brand transition-colors">
117
--- templates/dashboard.html
+++ templates/dashboard.html
@@ -106,11 +106,11 @@
106 <p class="mt-1 text-xs text-gray-500">Organize members into teams</p>
107 </a>
108 {% endif %}
109 {% if perms.pages.view_page %}
110 <a href="{% url 'pages:list' %}" class="block rounded-lg bg-gray-800 border border-gray-700 p-4 hover:border-brand transition-colors">
111 <h3 class="text-sm font-semibold text-gray-100">FossilRepo Docs</h3>
112 <p class="mt-1 text-xs text-gray-500">Guides, runbooks, documentation</p>
113 </a>
114 {% endif %}
115 {% if perms.organization.view_organization %}
116 <a href="{% url 'organization:settings' %}" class="block rounded-lg bg-gray-800 border border-gray-700 p-4 hover:border-brand transition-colors">
117
--- templates/fossil/wiki_list.html
+++ templates/fossil/wiki_list.html
@@ -50,12 +50,19 @@
5050
<div class="sticky top-6">
5151
<h3 class="text-xs font-semibold uppercase tracking-wider text-gray-500 mb-3">Wiki Pages</h3>
5252
<nav class="space-y-0.5">
5353
{% for p in pages %}
5454
<a href="{% url 'fossil:wiki_page' slug=project.slug page_name=p.name %}"
55
- class="block rounded-md px-3 py-1.5 text-sm text-gray-400 hover:text-gray-200 hover:bg-gray-800/50">
55
+ class="block rounded-md px-3 py-1.5 text-sm {% if p.name == 'Home' %}text-brand-light font-medium{% else %}text-gray-400{% endif %} hover:text-gray-200 hover:bg-gray-800/50">
56
+ {% if p.name == "Home" %}
57
+ <span class="flex items-center gap-1.5">
58
+ <svg class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12l8.954-8.955a1.126 1.126 0 011.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" /></svg>
59
+ {{ p.name }}
60
+ </span>
61
+ {% else %}
5662
{{ p.name }}
63
+ {% endif %}
5764
</a>
5865
{% endfor %}
5966
</nav>
6067
{% if not pages %}
6168
<p class="text-xs text-gray-600 px-3">No pages.</p>
6269
--- templates/fossil/wiki_list.html
+++ templates/fossil/wiki_list.html
@@ -50,12 +50,19 @@
50 <div class="sticky top-6">
51 <h3 class="text-xs font-semibold uppercase tracking-wider text-gray-500 mb-3">Wiki Pages</h3>
52 <nav class="space-y-0.5">
53 {% for p in pages %}
54 <a href="{% url 'fossil:wiki_page' slug=project.slug page_name=p.name %}"
55 class="block rounded-md px-3 py-1.5 text-sm text-gray-400 hover:text-gray-200 hover:bg-gray-800/50">
 
 
 
 
 
 
56 {{ p.name }}
 
57 </a>
58 {% endfor %}
59 </nav>
60 {% if not pages %}
61 <p class="text-xs text-gray-600 px-3">No pages.</p>
62
--- templates/fossil/wiki_list.html
+++ templates/fossil/wiki_list.html
@@ -50,12 +50,19 @@
50 <div class="sticky top-6">
51 <h3 class="text-xs font-semibold uppercase tracking-wider text-gray-500 mb-3">Wiki Pages</h3>
52 <nav class="space-y-0.5">
53 {% for p in pages %}
54 <a href="{% url 'fossil:wiki_page' slug=project.slug page_name=p.name %}"
55 class="block rounded-md px-3 py-1.5 text-sm {% if p.name == 'Home' %}text-brand-light font-medium{% else %}text-gray-400{% endif %} hover:text-gray-200 hover:bg-gray-800/50">
56 {% if p.name == "Home" %}
57 <span class="flex items-center gap-1.5">
58 <svg class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12l8.954-8.955a1.126 1.126 0 011.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" /></svg>
59 {{ p.name }}
60 </span>
61 {% else %}
62 {{ p.name }}
63 {% endif %}
64 </a>
65 {% endfor %}
66 </nav>
67 {% if not pages %}
68 <p class="text-xs text-gray-600 px-3">No pages.</p>
69
--- templates/includes/sidebar.html
+++ templates/includes/sidebar.html
@@ -85,33 +85,63 @@
8585
{% endif %}
8686
</div>
8787
</div>
8888
{% endif %}
8989
90
- <!-- FossilRepo KB section -->
91
- {% if perms.pages.view_page %}
92
- <div>
90
+ <!-- FossilRepo Docs (product docs — read-only) -->
91
+ {% if sidebar_product_docs %}
92
+ <div x-data="{ docsOpen: false }">
9393
<button @click="collapsed ? (collapsed = false, docsOpen = true) : (docsOpen = !docsOpen)"
9494
class="flex items-center justify-between w-full rounded-md px-2 py-2 text-sm font-medium {% if '/kb/' in request.path %}text-white{% else %}text-gray-400 hover:bg-gray-800 hover:text-white{% endif %}"
95
- :title="collapsed ? 'FossilRepo KB' : ''">
95
+ :title="collapsed ? 'FossilRepo Docs' : ''">
9696
<span class="flex items-center gap-2">
9797
<svg class="h-4 w-4 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
9898
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25" />
9999
</svg>
100
- <span x-show="!collapsed" class="truncate">FossilRepo KB</span>
100
+ <span x-show="!collapsed" class="truncate">FossilRepo Docs</span>
101101
</span>
102102
<svg x-show="!collapsed" class="h-4 w-4 transition-transform" :class="docsOpen && 'rotate-90'" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
103103
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
104104
</svg>
105105
</button>
106106
<div x-show="docsOpen && !collapsed" x-collapse class="ml-4 mt-1 space-y-0.5 border-l border-gray-700 pl-3">
107
- {% for p in sidebar_pages %}
107
+ {% for p in sidebar_product_docs %}
108
+ <a href="{% url 'pages:detail' slug=p.slug %}"
109
+ class="block rounded-md px-3 py-1.5 text-sm {% if p.slug in request.path %}text-brand-light font-medium{% else %}text-gray-500 hover:text-gray-300{% endif %} truncate">
110
+ {{ p.name }}
111
+ </a>
112
+ {% endfor %}
113
+ </div>
114
+ </div>
115
+ {% endif %}
116
+
117
+ <!-- Knowledge Base (org wiki — user-editable) -->
118
+ {% if perms.pages.view_page %}
119
+ <div x-data="{ kbOpen: false }">
120
+ <button @click="collapsed ? (collapsed = false, kbOpen = true) : (kbOpen = !kbOpen)"
121
+ class="flex items-center justify-between w-full rounded-md px-2 py-2 text-sm font-medium text-gray-400 hover:bg-gray-800 hover:text-white"
122
+ :title="collapsed ? 'Knowledge Base' : ''">
123
+ <span class="flex items-center gap-2">
124
+ <svg class="h-4 w-4 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
125
+ <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 9.776c.112-.017.227-.026.344-.026h15.812c.117 0 .232.009.344.026m-16.5 0a2.25 2.25 0 00-1.883 2.542l.857 6a2.25 2.25 0 002.227 1.932H19.05a2.25 2.25 0 002.227-1.932l.857-6a2.25 2.25 0 00-1.883-2.542m-16.5 0V6A2.25 2.25 0 016 3.75h3.879a1.5 1.5 0 011.06.44l2.122 2.12a1.5 1.5 0 001.06.44H18A2.25 2.25 0 0120.25 9v.776" />
126
+ </svg>
127
+ <span x-show="!collapsed" class="truncate">Knowledge Base</span>
128
+ </span>
129
+ <svg x-show="!collapsed" class="h-4 w-4 transition-transform" :class="kbOpen && 'rotate-90'" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
130
+ <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
131
+ </svg>
132
+ </button>
133
+ <div x-show="kbOpen && !collapsed" x-collapse class="ml-4 mt-1 space-y-0.5 border-l border-gray-700 pl-3">
134
+ {% for p in sidebar_kb_pages %}
108135
<a href="{% url 'pages:detail' slug=p.slug %}"
109136
class="block rounded-md px-3 py-1.5 text-sm {% if p.slug in request.path %}text-brand-light font-medium{% else %}text-gray-500 hover:text-gray-300{% endif %} truncate">
110137
{{ p.name }}
111138
</a>
112139
{% endfor %}
140
+ {% if sidebar_kb_pages|length == 0 %}
141
+ <p class="px-3 py-1.5 text-xs text-gray-600">No articles yet.</p>
142
+ {% endif %}
113143
{% if perms.pages.add_page %}
114144
<a href="{% url 'pages:create' %}"
115145
class="block rounded-md px-3 py-1.5 text-sm text-gray-600 hover:text-brand-light">
116146
+ New
117147
</a>
118148
--- templates/includes/sidebar.html
+++ templates/includes/sidebar.html
@@ -85,33 +85,63 @@
85 {% endif %}
86 </div>
87 </div>
88 {% endif %}
89
90 <!-- FossilRepo KB section -->
91 {% if perms.pages.view_page %}
92 <div>
93 <button @click="collapsed ? (collapsed = false, docsOpen = true) : (docsOpen = !docsOpen)"
94 class="flex items-center justify-between w-full rounded-md px-2 py-2 text-sm font-medium {% if '/kb/' in request.path %}text-white{% else %}text-gray-400 hover:bg-gray-800 hover:text-white{% endif %}"
95 :title="collapsed ? 'FossilRepo KB' : ''">
96 <span class="flex items-center gap-2">
97 <svg class="h-4 w-4 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
98 <path stroke-linecap="round" stroke-linejoin="round" d="M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25" />
99 </svg>
100 <span x-show="!collapsed" class="truncate">FossilRepo KB</span>
101 </span>
102 <svg x-show="!collapsed" class="h-4 w-4 transition-transform" :class="docsOpen && 'rotate-90'" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
103 <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
104 </svg>
105 </button>
106 <div x-show="docsOpen && !collapsed" x-collapse class="ml-4 mt-1 space-y-0.5 border-l border-gray-700 pl-3">
107 {% for p in sidebar_pages %}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108 <a href="{% url 'pages:detail' slug=p.slug %}"
109 class="block rounded-md px-3 py-1.5 text-sm {% if p.slug in request.path %}text-brand-light font-medium{% else %}text-gray-500 hover:text-gray-300{% endif %} truncate">
110 {{ p.name }}
111 </a>
112 {% endfor %}
 
 
 
113 {% if perms.pages.add_page %}
114 <a href="{% url 'pages:create' %}"
115 class="block rounded-md px-3 py-1.5 text-sm text-gray-600 hover:text-brand-light">
116 + New
117 </a>
118
--- templates/includes/sidebar.html
+++ templates/includes/sidebar.html
@@ -85,33 +85,63 @@
85 {% endif %}
86 </div>
87 </div>
88 {% endif %}
89
90 <!-- FossilRepo Docs (product docs — read-only) -->
91 {% if sidebar_product_docs %}
92 <div x-data="{ docsOpen: false }">
93 <button @click="collapsed ? (collapsed = false, docsOpen = true) : (docsOpen = !docsOpen)"
94 class="flex items-center justify-between w-full rounded-md px-2 py-2 text-sm font-medium {% if '/kb/' in request.path %}text-white{% else %}text-gray-400 hover:bg-gray-800 hover:text-white{% endif %}"
95 :title="collapsed ? 'FossilRepo Docs' : ''">
96 <span class="flex items-center gap-2">
97 <svg class="h-4 w-4 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
98 <path stroke-linecap="round" stroke-linejoin="round" d="M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25" />
99 </svg>
100 <span x-show="!collapsed" class="truncate">FossilRepo Docs</span>
101 </span>
102 <svg x-show="!collapsed" class="h-4 w-4 transition-transform" :class="docsOpen && 'rotate-90'" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
103 <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
104 </svg>
105 </button>
106 <div x-show="docsOpen && !collapsed" x-collapse class="ml-4 mt-1 space-y-0.5 border-l border-gray-700 pl-3">
107 {% for p in sidebar_product_docs %}
108 <a href="{% url 'pages:detail' slug=p.slug %}"
109 class="block rounded-md px-3 py-1.5 text-sm {% if p.slug in request.path %}text-brand-light font-medium{% else %}text-gray-500 hover:text-gray-300{% endif %} truncate">
110 {{ p.name }}
111 </a>
112 {% endfor %}
113 </div>
114 </div>
115 {% endif %}
116
117 <!-- Knowledge Base (org wiki — user-editable) -->
118 {% if perms.pages.view_page %}
119 <div x-data="{ kbOpen: false }">
120 <button @click="collapsed ? (collapsed = false, kbOpen = true) : (kbOpen = !kbOpen)"
121 class="flex items-center justify-between w-full rounded-md px-2 py-2 text-sm font-medium text-gray-400 hover:bg-gray-800 hover:text-white"
122 :title="collapsed ? 'Knowledge Base' : ''">
123 <span class="flex items-center gap-2">
124 <svg class="h-4 w-4 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
125 <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 9.776c.112-.017.227-.026.344-.026h15.812c.117 0 .232.009.344.026m-16.5 0a2.25 2.25 0 00-1.883 2.542l.857 6a2.25 2.25 0 002.227 1.932H19.05a2.25 2.25 0 002.227-1.932l.857-6a2.25 2.25 0 00-1.883-2.542m-16.5 0V6A2.25 2.25 0 016 3.75h3.879a1.5 1.5 0 011.06.44l2.122 2.12a1.5 1.5 0 001.06.44H18A2.25 2.25 0 0120.25 9v.776" />
126 </svg>
127 <span x-show="!collapsed" class="truncate">Knowledge Base</span>
128 </span>
129 <svg x-show="!collapsed" class="h-4 w-4 transition-transform" :class="kbOpen && 'rotate-90'" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
130 <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
131 </svg>
132 </button>
133 <div x-show="kbOpen && !collapsed" x-collapse class="ml-4 mt-1 space-y-0.5 border-l border-gray-700 pl-3">
134 {% for p in sidebar_kb_pages %}
135 <a href="{% url 'pages:detail' slug=p.slug %}"
136 class="block rounded-md px-3 py-1.5 text-sm {% if p.slug in request.path %}text-brand-light font-medium{% else %}text-gray-500 hover:text-gray-300{% endif %} truncate">
137 {{ p.name }}
138 </a>
139 {% endfor %}
140 {% if sidebar_kb_pages|length == 0 %}
141 <p class="px-3 py-1.5 text-xs text-gray-600">No articles yet.</p>
142 {% endif %}
143 {% if perms.pages.add_page %}
144 <a href="{% url 'pages:create' %}"
145 class="block rounded-md px-3 py-1.5 text-sm text-gray-600 hover:text-brand-light">
146 + New
147 </a>
148
--- templates/pages/page_form.html
+++ templates/pages/page_form.html
@@ -1,11 +1,11 @@
11
{% extends "base.html" %}
22
{% block title %}{{ title }} — Fossilrepo{% endblock %}
33
44
{% block content %}
55
<div class="mb-6">
6
- <a href="{% url 'pages:list' %}" class="text-sm text-brand-light hover:text-brand">&larr; Back to FossilRepo KB</a>
6
+ <a href="{% url 'pages:list' %}" class="text-sm text-brand-light hover:text-brand">&larr; Back to FossilRepo Docs</a>
77
</div>
88
99
<div class="mx-auto max-w-4xl">
1010
<h1 class="text-2xl font-bold text-gray-100 mb-6">{{ title }}</h1>
1111
1212
--- templates/pages/page_form.html
+++ templates/pages/page_form.html
@@ -1,11 +1,11 @@
1 {% extends "base.html" %}
2 {% block title %}{{ title }} — Fossilrepo{% endblock %}
3
4 {% block content %}
5 <div class="mb-6">
6 <a href="{% url 'pages:list' %}" class="text-sm text-brand-light hover:text-brand">&larr; Back to FossilRepo KB</a>
7 </div>
8
9 <div class="mx-auto max-w-4xl">
10 <h1 class="text-2xl font-bold text-gray-100 mb-6">{{ title }}</h1>
11
12
--- templates/pages/page_form.html
+++ templates/pages/page_form.html
@@ -1,11 +1,11 @@
1 {% extends "base.html" %}
2 {% block title %}{{ title }} — Fossilrepo{% endblock %}
3
4 {% block content %}
5 <div class="mb-6">
6 <a href="{% url 'pages:list' %}" class="text-sm text-brand-light hover:text-brand">&larr; Back to FossilRepo Docs</a>
7 </div>
8
9 <div class="mx-auto max-w-4xl">
10 <h1 class="text-2xl font-bold text-gray-100 mb-6">{{ title }}</h1>
11
12
--- templates/pages/page_list.html
+++ templates/pages/page_list.html
@@ -1,11 +1,11 @@
11
{% extends "base.html" %}
2
-{% block title %}FossilRepo KB — Fossilrepo{% endblock %}
2
+{% block title %}FossilRepo Docs — Fossilrepo{% endblock %}
33
44
{% block content %}
55
<div class="md:flex md:items-center md:justify-between mb-6">
6
- <h1 class="text-2xl font-bold text-gray-100">FossilRepo KB</h1>
6
+ <h1 class="text-2xl font-bold text-gray-100">FossilRepo Docs</h1>
77
{% if perms.pages.add_page %}
88
<a href="{% url 'pages:create' %}"
99
class="mt-4 md:mt-0 inline-flex items-center rounded-md bg-brand px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-brand-hover">
1010
New Page
1111
</a>
1212
--- templates/pages/page_list.html
+++ templates/pages/page_list.html
@@ -1,11 +1,11 @@
1 {% extends "base.html" %}
2 {% block title %}FossilRepo KB — Fossilrepo{% endblock %}
3
4 {% block content %}
5 <div class="md:flex md:items-center md:justify-between mb-6">
6 <h1 class="text-2xl font-bold text-gray-100">FossilRepo KB</h1>
7 {% if perms.pages.add_page %}
8 <a href="{% url 'pages:create' %}"
9 class="mt-4 md:mt-0 inline-flex items-center rounded-md bg-brand px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-brand-hover">
10 New Page
11 </a>
12
--- templates/pages/page_list.html
+++ templates/pages/page_list.html
@@ -1,11 +1,11 @@
1 {% extends "base.html" %}
2 {% block title %}FossilRepo Docs — Fossilrepo{% endblock %}
3
4 {% block content %}
5 <div class="md:flex md:items-center md:justify-between mb-6">
6 <h1 class="text-2xl font-bold text-gray-100">FossilRepo Docs</h1>
7 {% if perms.pages.add_page %}
8 <a href="{% url 'pages:create' %}"
9 class="mt-4 md:mt-0 inline-flex items-center rounded-md bg-brand px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-brand-hover">
10 New Page
11 </a>
12

Keyboard Shortcuts

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