Skip to content

Commit 8c19198

Browse files
committed
Initial setup — telemetry Worker, D1 schema, and docs
- Cloudflare Worker at telemetry.shackdesk.com/report - D1 schema for the shackdesk-telemetry database - GitHub Actions deployment workflow via Wrangler - README, SECURITY, PRIVACY, and MAINTENANCE documentation
0 parents  commit 8c19198

10 files changed

Lines changed: 502 additions & 0 deletions

File tree

.editorconfig

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
root = true
2+
3+
[*]
4+
charset = utf-8
5+
end_of_line = lf
6+
indent_style = space
7+
indent_size = 2
8+
insert_final_newline = true
9+
trim_trailing_whitespace = true
10+
11+
[*.md]
12+
trim_trailing_whitespace = false
13+
14+
[*.toml]
15+
indent_size = 2

.github/workflows/deploy.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: Deploy Workers
2+
3+
on:
4+
push:
5+
branches: [main]
6+
workflow_dispatch:
7+
8+
jobs:
9+
deploy-telemetry:
10+
name: Deploy Telemetry Worker
11+
runs-on: ubuntu-latest
12+
permissions:
13+
contents: read
14+
15+
steps:
16+
- name: Checkout
17+
uses: actions/checkout@v4
18+
19+
- name: Deploy Worker
20+
uses: cloudflare/wrangler-action@v3
21+
with:
22+
apiToken: ${{ secrets.CF_API_TOKEN }}
23+
accountId: ${{ secrets.CF_ACCOUNT_ID }}

.gitignore

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Wrangler local state and dev database
2+
.wrangler/
3+
4+
# Local secrets — never commit this file
5+
# See README.md for the required format
6+
.dev.vars
7+
8+
# Node
9+
node_modules/
10+
npm-debug.log*
11+
12+
# OS
13+
.DS_Store
14+
Thumbs.db
15+
16+
# AI assistant working files
17+
CLAUDE.md
18+
.claude/
19+
*.claude

MAINTENANCE.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Maintenance Guide — ShackDesk Backend
2+
3+
## Cloudflare API Token
4+
5+
The `CF_API_TOKEN` GitHub Actions secret authorises Wrangler to deploy Workers.
6+
7+
**To rotate:**
8+
1. Cloudflare Dashboard → **My Profile → API Tokens → Create Token**
9+
2. Use the **Edit Cloudflare Workers** template
10+
3. Add D1 write permission if not included
11+
4. Copy the token
12+
5. GitHub → `Computer-Consultant/ShackDesk-Backend`**Settings → Secrets → Actions**
13+
6. Update `CF_API_TOKEN`
14+
7. Delete the old token from the Cloudflare dashboard
15+
16+
**CF_ACCOUNT_ID** is not a secret but is stored as a GitHub secret for convenience. Find it on the Cloudflare dashboard right sidebar of any zone page.
17+
18+
## D1 Database
19+
20+
**Database name:** `shackdesk-telemetry`
21+
**Database ID:** `e77c8a3d-cba7-4326-846c-faeb2c585da0`
22+
23+
### Query the database
24+
```bash
25+
wrangler d1 execute shackdesk-telemetry --remote --command="SELECT * FROM reports ORDER BY received_at DESC LIMIT 20;"
26+
```
27+
28+
### Data retention — manual purge (90-day policy)
29+
```bash
30+
wrangler d1 execute shackdesk-telemetry --remote \
31+
--command="DELETE FROM reports WHERE received_at < datetime('now', '-90 days');"
32+
```
33+
34+
This should be run periodically. Automating it via a Cloudflare Cron Trigger is a future improvement.
35+
36+
### Schema migrations
37+
38+
Schema changes require a migration file. Never alter the live schema directly without testing locally first.
39+
40+
1. Write the migration SQL
41+
2. Test locally: `wrangler d1 execute shackdesk-telemetry --local --file=migration.sql`
42+
3. Apply to production: `wrangler d1 execute shackdesk-telemetry --remote --file=migration.sql`
43+
4. Commit the migration file to the repo under `db/migrations/`
44+
45+
## Adding a New Worker
46+
47+
1. Create `workers/<name>/src/index.js`
48+
2. Add a new `[[routes]]` block and any bindings to `wrangler.toml`
49+
3. If the new Worker needs its own D1 database, create it: `wrangler d1 create <db-name>`
50+
4. Add a deploy step to `.github/workflows/deploy.yml` if it needs a separate pipeline
51+
52+
## Local Development
53+
54+
```bash
55+
wrangler dev
56+
```
57+
58+
Wrangler creates a local SQLite database at `.wrangler/state/`. This is gitignored.
59+
60+
To seed the local database with the schema:
61+
```bash
62+
wrangler d1 execute shackdesk-telemetry --local --file=db/schema.sql
63+
```
64+
65+
## DNS
66+
67+
`telemetry.shackdesk.com` — CNAME record in the Cloudflare zone, proxied (orange cloud).
68+
Wrangler registers the route automatically on deploy via `wrangler.toml`. If the route ever disappears, redeploy: `wrangler deploy`.
69+
70+
## Cloudflare Rate Limiting
71+
72+
Rate limiting is not in code — it is configured in the Cloudflare dashboard.
73+
74+
**Location:** Cloudflare Dashboard → shackdesk.com zone → **Security → WAF → Rate Limiting Rules**
75+
76+
Recommended rule for `/report`:
77+
- Expression: `http.request.uri.path eq "/report"`
78+
- Threshold: 30 requests per 60 seconds
79+
- Action: Block (duration: 60 seconds)

PRIVACY.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Privacy Policy — ShackDesk Backend
2+
3+
**Last updated:** 2026-04-06
4+
5+
This document describes what data the ShackDesk backend services collect, store, and retain. It is intended to be read alongside the privacy policy of each ShackDesk application.
6+
7+
## Telemetry Endpoint (`POST /report`)
8+
9+
### What is collected
10+
11+
When a ShackDesk application sends a telemetry report, the following fields are stored:
12+
13+
| Field | Example | Purpose |
14+
|--------------------|----------------------------------|--------------------------------|
15+
| `report_id` | UUID | Deduplication |
16+
| `app` | `PortPane` | Identify the source application|
17+
| `version` | `0.5.2-alpha` | Track version distribution |
18+
| `event` | `crash` | Event classification |
19+
| `os` | `Windows 10.0.19045` | OS version analytics |
20+
| `props` | `{"exception":"...", ...}` | Event-specific detail |
21+
| `client_timestamp` | `2026-04-06T12:00:00Z` | When the event occurred |
22+
| `received_at` | `2026-04-06T12:00:05Z` | When the server received it |
23+
24+
### What is never collected
25+
26+
- IP addresses — not stored, not hashed, not logged
27+
- User identity — no callsign, name, or account
28+
- File paths or directory names
29+
- Device serial numbers or MAC addresses
30+
- Any other personally identifiable information
31+
32+
### Opt-in only
33+
34+
Telemetry is disabled by default in all ShackDesk applications. Users must explicitly enable it in Settings. It can be disabled at any time.
35+
36+
## Data Storage and Retention
37+
38+
- Data is stored in a Cloudflare D1 database (SQLite) hosted in Cloudflare's infrastructure
39+
- All records are retained for a maximum of **90 days**
40+
- No data is sold, shared, or transferred to third parties
41+
42+
## Contact
43+
44+
For privacy questions, open an issue at:
45+
`https://github.com/Computer-Consultant/ShackDesk-Backend/issues`

README.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# ShackDesk-Backend
2+
3+
Backend services for ShackDesk desktop applications — hosted on Cloudflare Workers with D1 storage.
4+
5+
## Services
6+
7+
### Telemetry Worker (`workers/telemetry/`)
8+
9+
Accepts anonymous telemetry reports from ShackDesk apps via `POST https://telemetry.shackdesk.com/report`.
10+
11+
- No IP addresses stored
12+
- No personally identifiable information collected
13+
- Opt-in only — users must explicitly enable telemetry in the app
14+
- All data retained for 90 days maximum
15+
16+
See [PRIVACY.md](PRIVACY.md) for the full data policy.
17+
18+
## Architecture
19+
20+
```
21+
ShackDesk App (Windows)
22+
23+
│ POST /report (HTTPS)
24+
25+
telemetry.shackdesk.com ←── Cloudflare Worker (this repo)
26+
27+
│ INSERT
28+
29+
D1: shackdesk-telemetry
30+
└── reports table
31+
```
32+
33+
## Local Development
34+
35+
**Prerequisites:** Node.js 18+, Wrangler CLI (`npm install -g wrangler`)
36+
37+
**Authenticate:**
38+
```
39+
wrangler login
40+
```
41+
42+
**Create a `.dev.vars` file** (never committed — see `.gitignore`):
43+
```
44+
# No secrets required for the telemetry Worker currently.
45+
# Future Workers may require API tokens here.
46+
```
47+
48+
**Run locally:**
49+
```
50+
wrangler dev
51+
```
52+
53+
This starts the Worker on `http://localhost:8787`. Wrangler creates a local SQLite database that mirrors the D1 schema for development.
54+
55+
**Test with curl:**
56+
```bash
57+
curl -X POST http://localhost:8787/report \
58+
-H "Content-Type: application/json" \
59+
-d '{
60+
"report_id": "test-guid-1234",
61+
"app": "PortPane",
62+
"version": "0.5.2-alpha",
63+
"event": "crash",
64+
"os": "Windows 10.0.19045",
65+
"timestamp": "2026-04-06T00:00:00Z",
66+
"props": { "exception": "NullReferenceException", "message": "Test" }
67+
}'
68+
```
69+
70+
## Deployment
71+
72+
Deployment is automatic on push to `main` via GitHub Actions (see `.github/workflows/deploy.yml`).
73+
74+
Manual deployment:
75+
```
76+
wrangler deploy
77+
```
78+
79+
**Required GitHub Secrets:**
80+
81+
| Secret | Description |
82+
|--------|-------------|
83+
| `CF_API_TOKEN` | Cloudflare API token with Workers and D1 write permissions |
84+
| `CF_ACCOUNT_ID` | Cloudflare account ID |
85+
86+
See [MAINTENANCE.md](MAINTENANCE.md) for how to create and rotate these.
87+
88+
## DNS
89+
90+
`telemetry.shackdesk.com` must have a DNS CNAME record in the Cloudflare zone pointing at the Worker route. This is managed via `wrangler.toml` — Wrangler registers the route automatically on deploy. The DNS record must be proxied (orange cloud) in the Cloudflare dashboard.
91+
92+
## Repo Structure
93+
94+
```
95+
ShackDesk-Backend/
96+
workers/
97+
telemetry/
98+
src/
99+
index.js # Worker entry point
100+
wrangler.toml # Cloudflare Worker config
101+
.github/
102+
workflows/
103+
deploy.yml # CI deployment pipeline
104+
README.md
105+
SECURITY.md
106+
MAINTENANCE.md
107+
PRIVACY.md
108+
.gitignore
109+
.editorconfig
110+
```

SECURITY.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Security Policy
2+
3+
## Reporting a Vulnerability
4+
5+
If you discover a security vulnerability in ShackDesk-Backend, please report it privately.
6+
7+
**Do not open a public GitHub issue for security vulnerabilities.**
8+
9+
Open a private security advisory at:
10+
`https://github.com/Computer-Consultant/ShackDesk-Backend/security/advisories/new`
11+
12+
Please include:
13+
- Description of the vulnerability
14+
- Steps to reproduce
15+
- Potential impact
16+
17+
You can expect an acknowledgement within 72 hours and a resolution or mitigation plan within 14 days.
18+
19+
## Security Design
20+
21+
### No PII Stored
22+
23+
The telemetry Worker explicitly does not store:
24+
- IP addresses (not even hashed)
25+
- User identifiers or callsigns
26+
- File paths or system usernames
27+
- Device serial numbers or MAC addresses
28+
29+
### Rate Limiting
30+
31+
Rate limiting is enforced at the Cloudflare zone level, not in Worker code. This avoids any need to track or store client IPs. Configure rate limiting rules in the Cloudflare dashboard under **Security → WAF → Rate Limiting Rules**.
32+
33+
Recommended rule:
34+
- Path: `/report`
35+
- Threshold: 30 requests per minute per IP
36+
- Action: Block for 1 minute
37+
38+
### Input Validation
39+
40+
All incoming payloads are validated for:
41+
- Required fields presence and type
42+
- Field length limits (prevents oversized payloads)
43+
- Valid JSON structure
44+
45+
### Payload Size
46+
47+
The `props` field is capped at 4 KB. The Worker returns 400 if the limit is exceeded.
48+
49+
### Duplicate Reports
50+
51+
The `report_id` field is the D1 primary key. Duplicate submissions (e.g. from the app's offline queue retrying) are silently ignored via `ON CONFLICT DO NOTHING`.
52+
53+
### Transport Security
54+
55+
All traffic is HTTPS only. The `telemetry.shackdesk.com` subdomain is served through Cloudflare's proxy (orange cloud), which enforces TLS 1.2+ and provides DDoS protection.
56+
57+
### Secrets Management
58+
59+
No secrets are stored in code or `wrangler.toml`. Cloudflare API tokens for deployment are stored as GitHub Actions secrets only. Rotate them via the Cloudflare dashboard — see [MAINTENANCE.md](MAINTENANCE.md).
60+
61+
## Supported Versions
62+
63+
Only the current `main` branch deployment is supported. There are no versioned releases of the backend.

db/schema.sql

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
-- ShackDesk Backend — D1 Schema
2+
-- Database: shackdesk-telemetry
3+
--
4+
-- Apply to production: wrangler d1 execute shackdesk-telemetry --remote --file=db/schema.sql
5+
-- Apply locally: wrangler d1 execute shackdesk-telemetry --local --file=db/schema.sql
6+
--
7+
-- This file reflects the current live schema.
8+
-- For changes, add a migration file under db/migrations/ and update this file to match.
9+
10+
CREATE TABLE IF NOT EXISTS reports (
11+
id TEXT PRIMARY KEY, -- report_id GUID from the app payload
12+
app TEXT NOT NULL, -- e.g. "PortPane", "RigCheck"
13+
version TEXT NOT NULL, -- app version string
14+
event TEXT NOT NULL, -- e.g. "crash", "device_detection", "startup"
15+
os TEXT, -- Windows version string
16+
props TEXT, -- JSON blob — event-specific fields
17+
client_timestamp TEXT, -- ISO 8601 UTC — when the event occurred
18+
received_at TEXT NOT NULL -- ISO 8601 UTC — when the Worker received it
19+
);
20+
21+
CREATE INDEX IF NOT EXISTS idx_app ON reports (app);
22+
CREATE INDEX IF NOT EXISTS idx_event ON reports (event);
23+
CREATE INDEX IF NOT EXISTS idx_received ON reports (received_at);

0 commit comments

Comments
 (0)