# 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= ↓ 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 ` **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 ` **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 ` **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 ` **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=` 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-.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_", "ids": [""], "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/ ``` --- ## 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)