Cinder API Reference
Cinder API Reference
The Cinder API exposes project heat data from your local dev directory. It runs on a Docker Bun server (port 4242) and is accessible over Tailscale from any device on your tailnet.
This is the public-facing reference. The same data is available machine-readable at /api/llms.txt (full) and /api/llms-mini.txt (iOS Shortcuts cheatsheet).
Base URLs
Local: http://localhost:4242
Tailscale: https://weixiangs-mac-mini.tail1ef495.ts.net:8445
The Tailscale URL uses a Let’s Encrypt cert (trusted by iOS Safari and Shortcuts without any configuration). It’s tailnet-only — your devices need Tailscale running.
Authentication
GET endpoints are public. POST endpoints require one of:
Header: X-Cinder-Key: YOUR_KEY
Header: Authorization: Bearer YOUR_KEY
Query: ?key=YOUR_KEY
The API key is generated on first run and stored in /data/state.json. Find it by checking the server startup log or reading the state file.
GET /api/health
Quick liveness check.
{
"success": true,
"message": "Cinder API running · 39 projects loaded"
}
GET /api/digest
The main endpoint. Human-readable summary of your project heat state. Best for widgets and Shortcuts.
{
"headline": "Blazing: 4 · Hot: 19 · Warm: 2 · Cold: 9",
"summary": "4 projects blazing. 9 going cold.",
"mostActive": "bythewei-dev",
"mostUrgent": "CAR",
"needsAttention": ["CAR", "algo-trading-python", "floraquest"],
"hotProjects": ["bythewei-dev", "cinder", "fire-my-lawyer"],
"totalActive": 39,
"totalArchived": 0,
"generatedAt": "2026-04-02T17:11:00.000Z"
}
needsAttention and hotProjects are capped at 5. mostUrgent is the oldest project (by last commit date) in the Cold/Ash tier.
GET /api/stats
Heat breakdown and swipe history counts.
{
"totalProjects": 39,
"archivedProjects": 0,
"totalReignited": 4,
"heatBreakdown": {
"blazing": 4,
"hot": 19,
"warm": 2,
"cooling": 0,
"cold": 9,
"ash": 5
},
"generatedAt": "2026-04-02T17:11:00.000Z"
}
GET /api/projects
All active (non-archived, non-snoozed) projects as an array.
Query params:
| Param | Example | Description |
|---|---|---|
heat | ?heat=Blazing | Filter by heat level (case-insensitive) |
limit | ?limit=5 | Max results |
Project object:
{
"id": "/Users/wei/Local_Dev/projects/cinder",
"name": "cinder",
"path": "/Users/wei/Local_Dev/projects/cinder",
"heat": "Blazing",
"dormantDays": 0,
"lastCommitDate": "2026-04-02T17:00:00.000Z",
"commitCountLastMonth": 83,
"currentBranch": "fire-my-lawyer",
"uncommittedChanges": 3,
"recentCommits": [
{
"hash": "41a9b41",
"message": "Update Cinder post: iOS daily standup",
"author": "Wei Zhang",
"date": "2026-04-02T17:11:00.000Z"
}
],
"techStack": ["swift", "typescript", "bun", "docker"],
"description": null
}
GET /api/projects/hot
Shorthand for ?heat=Blazing + ?heat=Hot combined. Returns the active projects.
GET /api/projects/cold
Cold and Ash tier only. Use for shame-based motivation.
GET /api/projects/random
One random active project. Useful for a “what should I work on today” Shortcut.
GET /api/projects/:name
Single project by name. Name matching is case-insensitive and matches either the project name or the directory basename.
GET /api/projects/cinder
GET /api/projects/Cinder # same result
Returns 404 JSON if not found.
GET /api/skills
The Claude Code skills available for project re-entry, grouped by category.
[
{ "id": "load", "slash": "/load", "label": "Load Context", "category": "Jump Back In" },
{ "id": "sprint-plan","slash": "/sprint-plan","label": "Sprint Plan", "category": "Plan" },
{ "id": "analyze", "slash": "/analyze", "label": "Analyze", "category": "Inspect" }
]
POST /api/projects/:name/reignite
Mark a project as reignited. Clears its cold status in the swipe record.
// body: {}
// response:
{ "success": true, "message": "Reignited cinder" }
POST /api/projects/:name/snooze
Hide a project from the active list for N days.
// body:
{ "days": 7 }
// response:
{ "success": true, "message": "Snoozed cinder" }
Default is 7 days if days is not provided.
POST /api/projects/:name/archive
Permanently hide a project. Append-only — edit state.json to undo.
// body: {}
// response:
{ "success": true, "message": "Archived old-side-project" }
POST /api/refresh
Force rescan of the projects directory. Busts the 5-minute cache.
{ "success": true, "message": "Scanned 39 projects" }
Heat Levels
Heat is derived from days since the last git commit:
| Level | Days | Notes |
|---|---|---|
| Blazing | 0–3 | Active, committed this week |
| Hot | 3–7 | Still warm |
| Warm | 7–30 | Recent month |
| Cooling | 30–90 | Getting distant |
| Cold | 90–180 | Needs attention |
| Ash | 180+ | It’s just sitting there |
Projects with no git history are excluded from heat calculations.
LLM Reference Endpoints
Two plain-text endpoints for LLM consumption:
GET /api/llms.txt Full project docs — for LLMs building on the API
GET /api/llms-mini.txt Compact cheatsheet — for iOS Shortcuts automation recipes
Both are public GET endpoints. Content is generated at request time with live stats injected.
iOS Shortcuts Integration
The recommended pattern for connecting iOS Shortcuts to Cinder:
Daily digest (read-only):
1. Get Contents of URL → https://...ts.net:8445/api/digest
2. Get Dictionary from Input
3. Get Value for Key "headline"
4. Show Result / Speak Text / Update Draft
The Drafts widget pattern:
The Cinder daily standup uses Drafts (by Agile Tortoise) as a persistent home screen display surface:
- Shortcut fetches
/api/digestat sunrise via an automation - First AI pass: Claude summarizes for text-to-speech (natural language)
- Shortcut speaks the summary
- Second AI pass: formats for widget display (max 11 lines, no ASCII, no headers)
Update Draftaction overwrites a pinned draft by UUID (Replace mode)- Drafts widget on home screen reflects the update within ~15 minutes
Why Drafts instead of a Shortcut widget: Shortcuts widgets have limited display options. The Drafts Draft Widget shows arbitrary text content from one named draft, updates automatically when the draft changes, and requires zero extra code. You’re using it as a named, writable string that iOS can display.
The two-model approach (Claude for speech, separate model for widget) solves a real constraint: text-to-speech output wants natural sentence flow, but widget display wants minimal ASCII and hard line limits. One prompt can’t optimize for both.
Running Locally
Docker (recommended — runs independently of Xcode):
cd cinder/api
docker compose up -d
Direct (Bun):
PROJECTS_DIR=~/Local_Dev/projects bun run api/server.ts
Environment variables:
| Var | Default | Description |
|---|---|---|
PORT | 4242 | Server port |
PROJECTS_DIR | /projects | Directory to scan |
DATA_FILE | /data/state.json | Persistence file |
Tailscale Funnel Setup
To expose the API with a trusted HTTPS cert (required for iOS Shortcuts):
# Check existing serves first — don't clobber other projects
tailscale serve status
# Add Cinder on an unused port (8445 in this setup)
tailscale serve --bg --https=8445 4242
# Result:
# https://weixiangs-mac-mini.tail1ef495.ts.net:8445 (tailnet only)
# |-- / proxy http://127.0.0.1:4242
Tailscale Funnel only supports three external ports for public internet access (443, 8443, 10000). If those are taken by other projects, use tailscale serve (tailnet-only) instead — your iOS devices get a trusted cert as long as Tailscale is running on them.