krz/domain-dig

an ios app for DNS & SSL analysis

clone: git clone https://gitbay.org/krz/domain-dig.git

v5.0.1: Docs/local-api.md · raw

  1# DomainDig Local API — `v1`
  2
  3The Local API exposes DomainDig's canonical report data to on-device automation
  4(Shortcuts, scripts, integrations). It is **off by default** and, when enabled,
  5binds only to loopback.
  6
  7- **Base URL:** `http://127.0.0.1:<port>` (default port `47821`, configurable in
  8  Settings → Local API)
  9- **Binding:** loopback only (`acceptLocalOnly`); never reachable off-device
 10- **Content type:** every response is `application/json`
 11- **Version:** `v1` (reported in every response envelope)
 12
 13This document is the stable contract. The response shape is pinned by
 14`DomainDigTests/LocalAPIContractTests.swift`; `LocalAPIContract` (in
 15`LocalAPIContract.swift`) is the single source of truth for the version string
 16and the JSON encoder.
 17
 18## Authentication
 19
 20Every request requires the token shown in Settings → Local API, supplied either
 21way:
 22
 23```
 24Authorization: Bearer <token>
 25```
 26```
 27X-API-Token: <token>
 28```
 29
 30A missing or wrong token returns `401 unauthorized`. Settings → Local API has a
 31**Copy cURL Command** button that emits a ready-to-run authenticated request.
 32
 33## Response envelope
 34
 35Every response — success or error — is wrapped in the same envelope:
 36
 37```json
 38{
 39  "success": true,
 40  "version": "v1",
 41  "data": { "...": "payload, present on success" }
 42}
 43```
 44```json
 45{
 46  "success": false,
 47  "version": "v1",
 48  "error": { "code": "not_found", "message": "The requested Local API route does not exist." }
 49}
 50```
 51
 52- On success, `data` holds the endpoint payload and `error` is **omitted**.
 53- On failure, `error` holds a machine `code` plus a human `message`, and `data`
 54  is **omitted**.
 55
 56### Encoding conventions
 57
 58- **Dates** are ISO-8601 UTC strings, e.g. `"2023-11-14T22:13:20Z"`.
 59- **Absent optional fields are omitted, not `null`.** Consumers must treat a
 60  missing key as "not present."
 61- Object keys are emitted in sorted order (deterministic output; not
 62  contractually meaningful — do not depend on key order).
 63
 64## Endpoints
 65
 66| Method | Path | Payload (`data`) fields |
 67|--------|------|-------------------------|
 68| GET | `/portfolio` | `summary``{ totalDomains, healthyCount, warningCount, criticalCount, changedLast24h, expiringSoonCount, unreachableCount }` |
 69| GET | `/domains` | `domains: [TrackedDomain]` |
 70| GET | `/domains/{domain}` | `domain`, `trackedDomain?` (`TrackedDomain`), `latestReport?` (`DomainReport`) |
 71| GET | `/domains/{domain}/history` | `domain`, `history: [HistoryEntry]` |
 72| GET | `/events` | `events: [{ timestamp, domain, summary, status, severity }]` |
 73| GET | `/monitoring` | `isEnabled`, `scope` (`"allTracked"` \| `"selectedOnly"`), `alertsEnabled`, `monitoredDomains: [{ domain, monitoringEnabled, lastMonitoredAt?, lastAlertAt?, certificateWarningLevel }]` |
 74| POST | `/inspect` | body `{ "domain": "example.com" }``report` (`DomainReport`) |
 75| POST | `/inspect/{domain}` | `report` (`DomainReport`) |
 76| POST | `/monitoring/{domain}/enable` | `domain`, `monitoringEnabled` |
 77| POST | `/monitoring/{domain}/disable` | `domain`, `monitoringEnabled` |
 78
 79`certificateWarningLevel` encodes as `"none"`, `"warning"`, or `"critical"`.
 80
 81`DomainReport` is the app's canonical report model (the same shape the JSON
 82export produces); see `DomainReportBuilder.swift` for its fields. It is a large
 83object and is treated as an additive contract: new fields may appear without a
 84version bump.
 85
 86## Error codes
 87
 88| HTTP | `code` | When |
 89|------|--------|------|
 90| 400 | `bad_request` | The HTTP request line/path could not be parsed |
 91| 400 | `invalid_body` | `POST /inspect` body was not `{ "domain": "…" }` |
 92| 400 | `invalid_domain` | A path/body domain was empty or invalid |
 93| 401 | `unauthorized` | Missing or incorrect token |
 94| 404 | `not_found` | No such route |
 95| 404 | `domain_not_found` | No local data / tracked domain for the given name |
 96| 500 | `encoding_failed` | The response could not be encoded |
 97| 500 | `internal_error` | The request handler failed unexpectedly |
 98
 99## Compatibility policy
100
101The `version` field follows a semantic-version-style promise:
102
103- **Backward-compatible changes keep `version` at `v1`.** Adding a new endpoint,
104  or adding a new field to an existing payload, is non-breaking. **Consumers
105  must ignore unknown fields.**
106- **Breaking changes bump `version`.** Renaming or removing a field, changing a
107  field's type, or changing the meaning/units of an existing field requires a new
108  version, an update to this document, and an update to
109  `LocalAPIContractTests.swift`.
110
111There are currently no deprecated fields or endpoints. When a field is
112deprecated, it will be listed here with the version in which it becomes eligible
113for removal, and will remain present for at least one subsequent version.