a control panel for my server
1# Control
2
3
4
5This servers as the source of truth for whether stuff is enabled or not.
6
7The canonical repo for this is hosted on tangled over at [`dunkirk.sh/con trol`](https://tangled.org/@dunkirk.sh/control)
8
9## Quick Start
10
11```bash
12bun install
13bun run dev
14```
15
16## Configuration
17
18### Environment Variables
19
20```bash
21# Server
22PORT=3010
23ORIGIN=http://localhost:3010 # or https://control.dunkirk.sh in production
24NODE_ENV=development # set to "production" for prod
25
26# OAuth (Indiko)
27INDIKO_URL=https://indiko.dunkirk.sh
28CLIENT_ID=https://control.dunkirk.sh/ # Must match Indiko client registration
29CLIENT_SECRET=<from-indiko-admin> # Required for role-based access
30
31# Session
32SESSION_SECRET=<random-32-bytes>
33
34# Flags directory
35FLAGS_DIR=/var/lib/caddy/flags
36
37# Optional: restrict to specific role
38REQUIRED_ROLE=admin
39```
40
41The `ORIGIN` is used to derive:
42
43- `CLIENT_ID`: `${ORIGIN}/` (if not set explicitly)
44- `REDIRECT_URI`: `${ORIGIN}/auth/callback`
45
46### Indiko Client Setup
47
48To use role-based access control, pre-register Control Panel as a client in Indiko:
49
501. Create a new client in Indiko admin
512. Set client ID to `https://control.dunkirk.sh/` (must match `CLIENT_ID` env)
523. Add `admin` to available roles
534. Copy the client secret to `CLIENT_SECRET`
545. Assign `admin` role to users who should access the control panel
55
56Control Panel publishes OAuth client metadata at `/client-metadata.json` for Indiko auto-discovery.
57
58### Flags Configuration
59
60Edit `flags.json` to define services and their flags:
61
62```json
63{
64 "services": {
65 "map.dunkirk.sh": {
66 "name": "Map",
67 "flags": {
68 "block-map-sse": {
69 "name": "Block SSE Endpoint",
70 "description": "Disable /sse Server-Sent Events"
71 }
72 }
73 }
74 }
75}
76```
77
78Each flag creates a marker file at `${FLAGS_DIR}/${flag-id}` when enabled.
79
80### Caddy Integration
81
82Configure Caddy to check for marker files:
83
84```
85@sse_blocked {
86 path /sse
87 file /var/lib/caddy/flags/block-map-sse
88}
89respond @sse_blocked "SSE temporarily disabled" 503
90```
91
92## API
93
94| Endpoint | Method | Description |
95| ----------------------- | ------ | ------------------------------------ |
96| `/` | GET | Dashboard UI |
97| `/auth/login` | GET | Start OAuth flow |
98| `/auth/callback` | GET | OAuth callback |
99| `/auth/logout` | POST | Clear session |
100| `/client-metadata.json` | GET | OAuth client metadata for Indiko |
101| `/api/session` | GET | Get current user info |
102| `/api/flags` | GET | List all flags |
103| `/api/flags/:name` | GET | Get flag status |
104| `/api/flags/:name` | PUT | Set flag (`{ enabled: true/false }`) |
105| `/api/flags/:name` | DELETE | Disable flag |
106
107<p align="center">
108 <img src="https://raw.githubusercontent.com/taciturnaxolotl/carriage/main/.github/images/line-break.svg" />
109</p>
110
111<p align="center">
112 <i><code>© 2025-present <a href="https://dunkirk.sh">Kieran Klukas</a></code></i>
113</p>
114
115<p align="center">
116 <a href="https://tangled.org/dunkirk.sh/control/blob/main/LICENSE.md"><img src="https://img.shields.io/static/v1.svg?style=for-the-badge&label=License&message=O'Saasy&logoColor=d9e0ee&colorA=363a4f&colorB=b7bdf8"/></a>
117</p>