- backend: /api/magic/{register,devices,profile,release} + AdGuard provisioning + 24h cooldown
- prisma: magic_binding_fields migration (additive on UserDevice)
- mac-app: Phase 2 - Login + MacRegistration + Profile install
- marketing: landing section + /download/rebreakmagic + DMG
- lyra: forbidden phrases + RebreakMagic coach guidance
267 lines
6.2 KiB
Markdown
267 lines
6.2 KiB
Markdown
# RebreakMagic Device-Binding — API Documentation
|
|
|
|
Backend-Implementation für DNS-basiertes Device-Binding via AdGuard Home DoH.
|
|
|
|
## Architektur-Überblick
|
|
|
|
```
|
|
RebreakMagic.app (Swift/macOS)
|
|
↓ POST /api/magic/register (JWT Auth)
|
|
↓
|
|
Backend (Nitro)
|
|
├─ DB: UserDevice (magicDnsToken, magicEnrolledAt, ...)
|
|
├─ AdGuard REST API: Create Persistent Client
|
|
└─ Response: { dnsToken, profileUrl }
|
|
↓
|
|
RebreakMagic.app → GET /api/magic/profile.mobileconfig?token=<dnsToken>
|
|
↓
|
|
macOS Configuration Profile (.mobileconfig)
|
|
↓ DNS-over-HTTPS: https://dns.rebreak.org/dns-query/{dnsToken}
|
|
↓
|
|
AdGuard Home (Hetzner) — Filtering + Logging per Client-ID
|
|
```
|
|
|
|
## Endpoints
|
|
|
|
### 1. `POST /api/magic/register`
|
|
Registriert Mac als Magic-Client, generiert DNS-Token, provisioniert AdGuard.
|
|
|
|
**Auth:** `Authorization: Bearer <jwt>`
|
|
|
|
**Body:**
|
|
```json
|
|
{
|
|
"deviceId": "550e8400-e29b-41d4-a716-446655440000",
|
|
"hostname": "Chahines MacBook Pro",
|
|
"model": "MacBookPro18,3",
|
|
"osVersion": "14.5"
|
|
}
|
|
```
|
|
|
|
**Response (Success):**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"deviceId": "550e8400-e29b-41d4-a716-446655440000",
|
|
"dnsToken": "QX7g9kL2mN4pR6tV8wY0zB3cD5fG7hJ9kM2nP4qS6uW8xZ0",
|
|
"profileUrl": "/api/magic/profile.mobileconfig?token=QX7g9kL2mN4pR6tV8wY0zB3cD5fG7hJ9kM2nP4qS6uW8xZ0",
|
|
"existing": false
|
|
}
|
|
}
|
|
```
|
|
|
|
**Response (Limit erreicht):**
|
|
```json
|
|
{
|
|
"statusCode": 409,
|
|
"message": "Magic-Device-Limit erreicht (max 3)",
|
|
"data": {
|
|
"code": "limit_reached",
|
|
"activeBindings": [
|
|
{
|
|
"deviceId": "...",
|
|
"hostname": "Mac #1",
|
|
"model": "MacBookPro18,3",
|
|
"osVersion": "14.5",
|
|
"magicEnrolledAt": "2026-06-01T10:00:00.000Z",
|
|
"releaseRequestedAt": null
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
**cURL:**
|
|
```bash
|
|
curl -X POST https://staging.rebreak.org/api/magic/register \
|
|
-H "Authorization: Bearer $JWT_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"deviceId": "550e8400-e29b-41d4-a716-446655440000",
|
|
"hostname": "Chahines MacBook Pro",
|
|
"model": "MacBookPro18,3",
|
|
"osVersion": "14.5"
|
|
}'
|
|
```
|
|
|
|
---
|
|
|
|
### 2. `GET /api/magic/devices`
|
|
Listet alle aktiven Magic-Bindings des Users.
|
|
|
|
**Auth:** `Authorization: Bearer <jwt>`
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": [
|
|
{
|
|
"deviceId": "550e8400-e29b-41d4-a716-446655440000",
|
|
"hostname": "Chahines MacBook Pro",
|
|
"model": "MacBookPro18,3",
|
|
"osVersion": "14.5",
|
|
"magicEnrolledAt": "2026-06-01T10:00:00.000Z",
|
|
"releaseRequestedAt": null,
|
|
"releaseAvailableAt": null
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**cURL:**
|
|
```bash
|
|
curl https://staging.rebreak.org/api/magic/devices \
|
|
-H "Authorization: Bearer $JWT_TOKEN"
|
|
```
|
|
|
|
---
|
|
|
|
### 3. `POST /api/magic/devices/:deviceId/request-release`
|
|
Startet 24h Cooldown für Device-Freigabe.
|
|
|
|
**Auth:** `Authorization: Bearer <jwt>`
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"releaseRequestedAt": "2026-06-01T10:00:00.000Z",
|
|
"releaseAvailableAt": "2026-06-02T10:00:00.000Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
**cURL:**
|
|
```bash
|
|
curl -X POST https://staging.rebreak.org/api/magic/devices/550e8400-e29b-41d4-a716-446655440000/request-release \
|
|
-H "Authorization: Bearer $JWT_TOKEN"
|
|
```
|
|
|
|
---
|
|
|
|
### 4. `POST /api/magic/devices/:deviceId/cancel-release`
|
|
Zieht Release-Request zurück.
|
|
|
|
**Auth:** `Authorization: Bearer <jwt>`
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": { "ok": true }
|
|
}
|
|
```
|
|
|
|
**cURL:**
|
|
```bash
|
|
curl -X POST https://staging.rebreak.org/api/magic/devices/550e8400-e29b-41d4-a716-446655440000/cancel-release \
|
|
-H "Authorization: Bearer $JWT_TOKEN"
|
|
```
|
|
|
|
---
|
|
|
|
### 5. `GET /api/magic/profile.mobileconfig?token=<dnsToken>`
|
|
Generiert personalisiertes macOS Configuration Profile.
|
|
|
|
**Auth:** KEINE (Token in Query-Parameter)
|
|
|
|
**Response-Headers:**
|
|
- `Content-Type: application/x-apple-aspen-config`
|
|
- `Content-Disposition: attachment; filename="RebreakMagic-<deviceId>.mobileconfig"`
|
|
|
|
**Response-Body:** XML-Plist (mobileconfig)
|
|
|
|
**cURL:**
|
|
```bash
|
|
curl "https://staging.rebreak.org/api/magic/profile.mobileconfig?token=QX7g9kL2mN4pR6tV8wY0zB3cD5fG7hJ9kM2nP4qS6uW8xZ0" \
|
|
-o RebreakMagic.mobileconfig
|
|
```
|
|
|
|
---
|
|
|
|
## DB-Schema
|
|
|
|
**UserDevice Model (Prisma Schema):**
|
|
```prisma
|
|
model UserDevice {
|
|
// ... existing fields ...
|
|
|
|
// RebreakMagic DNS-Device-Binding
|
|
magicDnsToken String? @unique @map("magic_dns_token")
|
|
magicEnrolledAt DateTime? @map("magic_enrolled_at")
|
|
magicRevokedAt DateTime? @map("magic_revoked_at")
|
|
magicHostname String? @map("magic_hostname")
|
|
}
|
|
```
|
|
|
|
**Migration:**
|
|
```bash
|
|
# User führt aus (NICHT auto-deployen):
|
|
pnpm prisma migrate dev --name magic_binding_fields
|
|
```
|
|
|
|
---
|
|
|
|
## AdGuard-Integration
|
|
|
|
**API-Endpoint:** `https://dns.rebreak.org/control/clients/add`
|
|
|
|
**Auth:** Basic Auth (`ADGUARD_USER`, `ADGUARD_PASSWORD`)
|
|
|
|
**Payload:**
|
|
```json
|
|
{
|
|
"name": "magic_<deviceId>",
|
|
"ids": ["<dnsToken>"],
|
|
"use_global_settings": false,
|
|
"filtering_enabled": true,
|
|
"parental_enabled": false,
|
|
"safebrowsing_enabled": true,
|
|
"blocked_services": []
|
|
}
|
|
```
|
|
|
|
**DoH-URL-Format (embedded in mobileconfig):**
|
|
```
|
|
https://dns.rebreak.org/dns-query/<dnsToken>
|
|
```
|
|
|
|
---
|
|
|
|
## Cron-Worker
|
|
|
|
**Funktion:** `processMagicReleases()` in `server/utils/magicCron.ts`
|
|
|
|
**Logic:**
|
|
1. Findet alle UserDevice mit `releaseRequestedAt < NOW() - 24h` AND `magicRevokedAt IS NULL`
|
|
2. Für jedes Device:
|
|
- DELETE AdGuard Client (`/control/clients/delete`)
|
|
- Setze `magicRevokedAt = NOW()`
|
|
3. Return `{ processed, errors }`
|
|
|
|
**Deployment:** TODO — Nitro Scheduled Task oder externer Cron-Trigger
|
|
|
|
---
|
|
|
|
## ENV-Variablen
|
|
|
|
Siehe [ENV_VARS.md](../ENV_VARS.md#rebreakmagic-dns-over-https-neu-2026-06-01):
|
|
|
|
- `ADGUARD_BASE_URL` — Default: `https://dns.rebreak.org`
|
|
- `ADGUARD_USER` — Admin-User für AdGuard Home REST API
|
|
- `ADGUARD_PASSWORD` — Admin-Password
|
|
|
|
---
|
|
|
|
## TODOs (Phase 2)
|
|
|
|
- [ ] Profile-Signierung via Apple Developer Certificate (`/usr/bin/security cms -S`)
|
|
- [ ] Cron-Registration für `processMagicReleases()` (Nitro scheduled task oder externer Cron)
|
|
- [ ] Plan-basierte Limits (jetzt hardcoded `MAGIC_DEVICE_LIMIT = 3`)
|
|
- [ ] AdGuard Blocked-Services konfigurieren (Gambling-Filter via AdGuard-Blocklisten)
|
|
- [ ] Tests (Phase 2: `rebreak-tester`)
|
|
- [ ] Frontend-Integration (RN-UI + RebreakMagic.app)
|