0ee6825a01
The ORM Model.toJSON() serializes only schema fields, so the mapped client_id/scopes/redirect_uris/... that OAuthClient.get() attaches to the wrapped Resource were stripped from GET /api/oauth/client[/:id] responses. client_id came back undefined; the theta-env bootstrap then POSTed /api/oauth/client/undefined/rotate and got a 500, aborting stack bring-up whenever proxy-secrets.js lacked a usable secret. - OAuthClient.get() now emits an explicit public toJSON (client_id, name, slug, scopes, redirect_uris, allowed_groups, token_lifetime, is_valid), deliberately omitting client_secret_hash so it can't leak over the API. - OAuthClient.get() null-guards Resource.get() (which returns null, not throws) and returns a clean 404 for an unknown/undefined id instead of crashing on r.kind. - Regression tests: list/get expose client_id + hide the secret hash, the list-then-rotate bootstrap path, and unknown-id -> 4xx not 500. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>