The iOS app on devopsdefender/dd-client#1 (commits 6bf998b, f4f6dab) lands a new GitHub-auth fleet flow, but it depends on CP-side endpoints that don't exist yet — https://app.devopsdefender.com/oauth/ios/start currently 404s and the new LaunchView's "Sign in with GitHub" tap opens an empty ASWebAuthenticationSession against that 404.
This issue is the CP-side contract the iOS client expects.
Required endpoints
GET /oauth/ios/start?pubkey=<hex>&label=<label>
- Begins GitHub OAuth.
- After GitHub redirects back, the CP records the (authenticated GitHub user) ↔
pubkey ↔ label mapping and issues a bearer token (JWT or opaque, server's choice).
- CP returns a 302 to:
devopsdefender://oauth/callback?token=<token> — the iOS app catches this via the existing devopsdefender:// custom-scheme registration (apps/ios/DevOpsDefender/Info.plist in dd-client).
pubkey is hex-encoded X25519 (64 chars); label is URL-encoded free text, defaults to UIDevice.current.name on the iOS side.
- This is also where enrollment happens — first call with a new pubkey associates it with the GitHub identity. Subsequent calls re-issue a token for the existing pubkey.
GET /api/v1/agents
- Auth:
Authorization: Bearer <token> from the OAuth flow above.
- Returns JSON array of agents the authenticated user has access to:
[
{
"id": "agent-stable-id",
"label": "Human-readable name, e.g. 'laptop'",
"agent_url": "https://agent-host.example.com",
"last_seen_at": "2026-05-11T14:48:00Z"
}
]
- 401/403 on missing/invalid token (iOS treats both as "re-auth needed" — wipes the Keychain token and returns to the launch chooser).
- Per-agent session listing does NOT need a CP endpoint — the iOS client uses the existing Noise
shell.list_sessions RPC on the agent directly once it has the agent URL. New FFI helper dd_client_list_sessions already wraps that.
Rebuild of /admin/enroll?pubkey=&label=
- Currently this is the URL the CLI's
dd-client keygen --cp-url … prints to enroll a desktop pubkey. Today it's a "useless webpage" per the iOS user.
- Rebuild it as the fleet dashboard — same destination iOS lands on after
/oauth/ios/start. When the URL carries pubkey/label query params (i.e. the user clicked the CLI's printed link), show a sticky banner at top: "Confirm enrollment of pubkey `abc…` as 'laptop'", with a confirm button.
- CLI output is unchanged (
enrollment_url in dd-client-core/src/lib.rs stays as-is) so existing scripts/docs keep working.
Agent-side authorization
- iOS does NOT carry a JWT through the Noise handshake. The CP is the source of truth for "which pubkeys can attach where."
- Agents are expected to poll the CP (or subscribe) for the list of authorized pubkeys at periodic intervals. When iOS attaches with its pubkey, the agent recognizes it from the freshest poll.
- Propagation window of a few seconds to a minute is fine for the iOS UX.
Token shape (open question)
iOS doesn't care whether the token is a JWT or opaque — it just needs:
- It's a single string returned as the `token=` query param in the 302 to `devopsdefender://oauth/callback`.
- It's accepted as `Authorization: Bearer ` on subsequent CP API calls.
- Expiry is up to the CP. iOS handles 401 by re-running the OAuth flow.
If you want it to be a JWT (e.g. so agents can verify it without a CP round-trip later), include the GitHub `sub`, the iOS pubkey hex, and an `exp`. iOS won't introspect it.
Why this matters now
Until these endpoints exist, the iOS fleet flow lands users on Cloudflare's 404 page inside the OAuth sheet — which they reasonably mistook for a crash today. The iOS side ships behind a `DEBUG_FAKE_FLEET` compile flag for now so we can iterate on the post-auth UI with stub agents, but real end-to-end requires CP work.
References
🤖 Generated with Claude Code
The iOS app on
devopsdefender/dd-client#1(commits6bf998b,f4f6dab) lands a new GitHub-auth fleet flow, but it depends on CP-side endpoints that don't exist yet —https://app.devopsdefender.com/oauth/ios/startcurrently 404s and the newLaunchView's "Sign in with GitHub" tap opens an emptyASWebAuthenticationSessionagainst that 404.This issue is the CP-side contract the iOS client expects.
Required endpoints
GET /oauth/ios/start?pubkey=<hex>&label=<label>pubkey↔labelmapping and issues a bearer token (JWT or opaque, server's choice).devopsdefender://oauth/callback?token=<token>— the iOS app catches this via the existingdevopsdefender://custom-scheme registration (apps/ios/DevOpsDefender/Info.plistindd-client).pubkeyis hex-encoded X25519 (64 chars);labelis URL-encoded free text, defaults toUIDevice.current.nameon the iOS side.GET /api/v1/agentsAuthorization: Bearer <token>from the OAuth flow above.[ { "id": "agent-stable-id", "label": "Human-readable name, e.g. 'laptop'", "agent_url": "https://agent-host.example.com", "last_seen_at": "2026-05-11T14:48:00Z" } ]shell.list_sessionsRPC on the agent directly once it has the agent URL. New FFI helperdd_client_list_sessionsalready wraps that.Rebuild of
/admin/enroll?pubkey=&label=dd-client keygen --cp-url …prints to enroll a desktop pubkey. Today it's a "useless webpage" per the iOS user./oauth/ios/start. When the URL carriespubkey/labelquery params (i.e. the user clicked the CLI's printed link), show a sticky banner at top: "Confirm enrollment of pubkey `abc…` as 'laptop'", with a confirm button.enrollment_urlindd-client-core/src/lib.rsstays as-is) so existing scripts/docs keep working.Agent-side authorization
Token shape (open question)
iOS doesn't care whether the token is a JWT or opaque — it just needs:
If you want it to be a JWT (e.g. so agents can verify it without a CP round-trip later), include the GitHub `sub`, the iOS pubkey hex, and an `exp`. iOS won't introspect it.
Why this matters now
Until these endpoints exist, the iOS fleet flow lands users on Cloudflare's 404 page inside the OAuth sheet — which they reasonably mistook for a crash today. The iOS side ships behind a `DEBUG_FAKE_FLEET` compile flag for now so we can iterate on the post-auth UI with stub agents, but real end-to-end requires CP work.
References
🤖 Generated with Claude Code