You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
On https://web.dtcdev.click, the owner can authenticate with one development-only local owner identity, use Django admin at /admin/ and the distinct custom Studio at /studio/, and issue a scoped, expiring, revocable Bearer token for a development automation service principal. Runtime Studio and /api/v1/admin/ expose credential list/create/rotate/revoke through the existing hashed APICredential services. The first automation token can call the implemented /api/v1/admin/health; the UI and API do not claim unavailable domain actions.
This is a rapid development vertical slice. It does not weaken or decide the production OIDC, MFA, break-glass, or final high-risk policy owned by #20/#28/#32/#33/#61.
Current deployed reality is intentionally empty of production-origin users, tokens, sessions, SocialApps, and SocialAccounts; the login page therefore reports no configured providers. The owner's production password/account cannot work in this development database unless a separately approved exact identity is loaded. This issue does not load it.
Canonical management surfaces
/admin/ remains Django admin for low-level model administration. Do not rename, replace, or style it as Studio.
/studio/ remains the custom management product over capability-registered application services.
/api/v1/admin/ remains the JSON automation API over the same services and permissions.
Safe GET/HEAD requests to /admin return one permanent redirect directly to /admin/; /studio does the same to /studio/. Preserve the query string, produce no redirect chain, and do not redirect unsafe methods.
Anonymous /admin/ follows Django admin's own login flow. Anonymous /studio/ redirects once to /accounts/login/?next=%2Fstudio%2F; authenticated but unauthorized requests return 403, not a misleading login loop.
Scope
Development owner bootstrap and login
Implement one secret-safe bootstrap interface for an authorized operator. The preferred application contract is an idempotent bootstrap_development_owner management command/service that:
refuses unless DTC_ENVIRONMENT=development; test code may exercise the underlying service only with explicit test settings;
accepts exactly one operator-confirmed owner email and a new development password via a non-echoing interactive channel, never a command-line argument, URL, committed file, issue comment, CI input/output, application log, or persisted bootstrap record;
creates exactly one accounts.CustomUser when absent, or resets only that same normalized identity after explicit reset confirmation; a conflicting existing identity or second owner attempt fails closed;
sets active/staff/superuser for Django admin, assigns the code-owned site_admin group for explicit Studio authority, and creates/updates the owner's linked human APIPrincipal with only the credential-management and health permissions registered by this issue;
creates/updates one named development automation service principal with only core.access_studio initially and no plaintext credential; the owner issues its first token in Studio;
on reset, revokes the owner's existing StaffSession rows and owner-human API credentials before accepting a new login; it does not silently revoke or recreate the separately visible service credential;
prints only a fixed success/failure category and safe counts—never email, password, token, digest, session key, database value, or provider material.
Document the exact authorized execution handoff without embedding a secret. If the deployed interactive execution mechanism is still under operational/AWS audit, engineering must still deliver and test this command/service interface; the operator invocation is [HUMAN] and must not broaden CI/deployer AWS permissions or block the code deployment.
On development only, /accounts/login/ adds a CSRF-protected local email/password form using the durable account backend. It uses generic invalid-credential responses and bounded throttling, preserves only a validated same-host next, rotates the Django session on success, and creates the existing revocable StaffSession for active staff. Production/local-test behavior does not silently enable routine local staff passwords.
Explicit Studio authority
is_staff/is_superuser permits Django admin as Django intends, but neither bypasses Studio authorization.
Studio requires active staff, an unrevoked StaffSession, and explicit site_admin permissions checked on every request.
Logout, owner disablement, role removal, session revocation, and owner password reset deny Studio on the next request.
The site_admin role gains one explicit credential-management permission; lower roles receive none.
Runtime credential workflows
Register non-test capabilities and readable CMP-based, line-broken Django templates for:
Action
Studio
Admin API
List safe credential metadata
GET /studio/access/api-credentials/
GET /api/v1/admin/credentials
Create for an allowed service principal
POST /studio/access/api-credentials/
POST /api/v1/admin/credentials
Rotate
POST /studio/access/api-credentials/<uuid>/rotate/
POST /api/v1/admin/credentials/<uuid>/rotate
Revoke
POST /studio/access/api-credentials/<uuid>/revoke/
POST /api/v1/admin/credentials/<uuid>/revoke
All adapters call shared management_auth query/command services through the capability registry; no adapter writes models directly. Target querysets are limited to active service principals the actor may manage. Missing, malformed, out-of-scope, human, and unauthorized targets do not leak object existence.
Create allows a safe name, an expiry within the existing 90-day maximum (default 30 days), and one or more non-test capability scopes that the target principal currently possesses. For this slice the development automation principal is entitled only to studio.home.read, so its first token can call GET /api/v1/admin/health. No wildcard, future implicit scope, or unimplemented action is shown. Future registered capabilities become selectable only after their owning issue grants the service principal permission and the owner explicitly issues a new scoped credential.
List responses contain only safe metadata: credential UUID, name, principal label/UUID, prefix, scopes, expiry, state, approximate last-used time, created time, and revision. They never expose the token, secret digest, password material, authorization header, or a recovery action.
Create and rotate reveal the raw token exactly once in the immediate successful response. Refresh, back navigation, idempotent replay, later GET/list, audit, database, logs, errors, OpenAPI examples, test artifacts, and screenshots cannot recover it. A create/rotate replay returns the existing safe result as secret_unavailable_on_replay without a token. Rotation preserves approved scopes, defaults to zero overlap, permits only an explicitly requested overlap up to the existing one-hour maximum, and marks the predecessor unusable after that bound. Revocation is immediate and safely idempotent.
Studio mutations require CSRF and explicit confirmation. Admin API create/rotate/revoke require Bearer authentication, the exact management scope/permission, confirmed: true, and Idempotency-Key; rotate/revoke also require the current revision (If-Match or the existing exact revision convention). Stale writes return 409 without mutation. Use existing bounded JSON, field allowlist, CORS-deny, rate, and safe error conventions.
Every allowed and denied create/rotate/revoke and bootstrap/reset action writes redacted audit evidence with actor, target snapshot, outcome, request/idempotency correlation, scopes, expiry/state, and safe reason. Never audit credentials, passwords, digests, headers, cookies, session keys, email values, or request bodies containing secrets.
Management response policy
Every response and redirect under /admin, /admin/, /studio, /studio/, /api/v1/admin/, and the development owner login flow is Cache-Control: private, no-store and X-Robots-Tag: noindex, nofollow; authenticated responses must not enter a public sitemap. Login, logout, denial, validation, one-time reveal, stale, and error states follow the same policy.
Non-goals
Importing any production user table, password hash, session, token, reset material, Allauth provider/app/account row, other user's PII, registration/activity data, or a broad production database.
Replacing Django admin with Studio, folding Studio into /admin/, exposing Django admin models through the admin API, or restoring legacy accounts.Token/plaintext CMP tokens.
Principal/role administration beyond the two bootstrap principals, domain CRUD, content sync, course/event/email actions, wildcard scopes, token recovery, production cutover, or AWS/IAM/Secrets Manager mutation.
Claiming the development automation token can perform any action not present in the runtime capability registry.
Acceptance criteria
One authorized development bootstrap creates/resets only the exact owner, assigns Django-admin flags plus explicit site_admin, creates the human and development service principals, leaks no identity/secret, and refuses production, noninteractive secret input, conflicts, and a second owner.
The owner can use the development login form, receives a fresh revocable StaffSession, reaches both distinct /admin/ and /studio/ surfaces, and loses Studio access immediately after logout, reset, disablement, role removal, or session revocation.
/admin and /studio perform exact one-hop permanent safe-method redirects to their slash routes with query preservation; unsafe methods do not redirect; canonical surfaces remain distinct.
Runtime capability registry, Studio, admin API, OpenAPI, permissions, services, audit, idempotency, concurrency, and results cover list/create/rotate/revoke with no test-only fixture route used at runtime.
The first service token is scoped only to studio.home.read, succeeds on /api/v1/admin/health, and is denied on missing scopes, credential-management operations, test-only operations, and unknown domain operations.
Secrets are one-time only and hashed at rest; list/replay/back/refresh/log/audit/error/database/artifact/screenshot checks expose no raw token, password, digest, cookie, or authorization header.
Expiry (default 30/max 90 days), zero/default and max-one-hour rotation overlap, immediate revocation, owner/principal disablement, permission removal, stale revision, replay, mass assignment, target scoping, throttling, and generic denials behave fail closed.
CSRF protects every Studio/login mutation; admin API mutations enforce Bearer scope, confirmation, idempotency, revision where applicable, bounded input/rate, and deny-by-default CORS.
All management/login responses are private/no-store/noindex and templates are CMP-based, readable, indented, line-broken, and non-minified.
Focused Django/auth/management/OpenAPI/parity tests and core Playwright pass; after independent tester and PM acceptance, normal main deployment proceeds automatically. The operator's real bootstrap invocation/login/token copy is reported separately as [HUMAN] without secret evidence.
Focused Django and security scenarios
Exercise bootstrap create, same-owner idempotent run, explicit reset, second/conflicting owner, production refusal, noninteractive refusal, rollback on failure, and output/log/audit secret canaries. Assert exact user/group/principal/session/credential counts and no production identity/provider/token rows.
POST valid/invalid local login with CSRF, throttling, hostile external next, duplicate normalized email, inactive/nonstaff/unassigned/superuser-only users, session fixation, logout, reset, revoke, and permission change. Studio never accepts flag-only authority.
Assert exact slashless redirect status/location/query/hop count for GET/HEAD and non-redirect behavior for POST, followed by canonical Django-admin versus Studio auth behavior.
Through both adapters, create/list/rotate/revoke the service credential; compare status, safe result, database effect, audit action, and permission decisions. Assert one-time token display and password-style digest verification without persisting raw secrets.
Replay create/rotate/revoke, reuse idempotency keys with changed payloads, race/stale revisions, expire/rotate/revoke/disable/remove permission, malformed IDs, human/out-of-scope targets, unknown/extra fields, invalid scopes/expiry/overlap, and rate exhaustion. Assert no existence leak or partial mutation.
Call /api/v1/admin/health with the first token, then prove missing/malformed/wrong/expired/revoked/rotated tokens and test-only/unregistered operations fail with the existing safe envelope. Regenerate OpenAPI and parity artifacts with only real runtime routes.
Scan response bodies, redirect locations, database rows, captured logs, audits, idempotency records, templates, OpenAPI, and test artifacts for token/password/digest/header/session/email canaries.
Browser acceptance
At desktop (~1440×900) and mobile (~390×844), using isolated synthetic identities only:
Visit /admin and /studio; verify direct slash redirects, distinct branding/purpose, correct login destination, no redirect loop, and private/noindex headers.
Log in through the development owner form with keyboard-only controls; verify safe invalid credentials, preserved local next, Studio navigation, separate Django admin, responsive layout, visible focus, and logout/back-cache denial.
In Studio list zero credentials, create the health-only automation token, verify the one-time warning/copy affordance without capturing the token, navigate away/back/refresh and confirm it cannot be revealed again.
Rotate with zero overlap, inspect safe predecessor/successor states, revoke the successor, and verify confirmation/cancel/stale/error/empty states. No secret appears in URL, page title, history, screenshot, or console/network diagnostics.
Use a separately injected synthetic token in the API client to prove health success then expiry/revocation denial; screenshots contain redacted placeholders only.
Store screenshots only under .tmp/screenshots/issue-107/, inspect each image, and post an independent tester report before PM acceptance.
Dependencies and delivery
Closed #86, #87, and #100 are sufficient implementation prerequisites. #20/#28/#32/#33/#61 are production-policy follow-ups, not blockers for this development-only owner slice. #99's safe review-data boundary is evidence that production identities are absent, not a dependency and not permission to import them.
Follow _docs/PROCESS.md: engineer implements/tests without committing; independent tester verifies and captures safe screenshots; PM accepts; engineer commits; orchestrator locally merges/pushes without a pull request; on-call alone observes CI/deployment. No secret may be used as lifecycle evidence.
Outcome
On
https://web.dtcdev.click, the owner can authenticate with one development-only local owner identity, use Django admin at/admin/and the distinct custom Studio at/studio/, and issue a scoped, expiring, revocable Bearer token for a development automation service principal. Runtime Studio and/api/v1/admin/expose credential list/create/rotate/revoke through the existing hashedAPICredentialservices. The first automation token can call the implemented/api/v1/admin/health; the UI and API do not claim unavailable domain actions.This is a rapid development vertical slice. It does not weaken or decide the production OIDC, MFA, break-glass, or final high-risk policy owned by #20/#28/#32/#33/#61.
Authority and current facts
_docs/specs/06-studio-and-admin-api.md: distinct management adapters, explicit permissions, Bearer tokens, one-time secrets, lifecycle, parity, audit, and private caching._docs/specs/07-security-privacy-operations.md: protected staff/session/API credentials, CSRF, redaction, and deny-by-default authorization._docs/specs/10-verification-strategy.md: staff denial and API-token one-time-display browser coverage.872fd8a9145a0e29de8890e9ea3e9d8df8c63cfd.StaffSession, audit, and private Studio middleware. Closed Build the decision-free admin API and service-principal foundation #87 providesAPIPrincipal, password-style hashedAPICredential, Bearer authentication, scope/permission checks, rate limits, idempotency, concurrency, OpenAPI/parity, and test-only credential lifecycle adapters/services. Closed Consolidate the adopted course account as the single website identity #100 preserves one durable website account model.Canonical management surfaces
/admin/remains Django admin for low-level model administration. Do not rename, replace, or style it as Studio./studio/remains the custom management product over capability-registered application services./api/v1/admin/remains the JSON automation API over the same services and permissions.GET/HEADrequests to/adminreturn one permanent redirect directly to/admin/;/studiodoes the same to/studio/. Preserve the query string, produce no redirect chain, and do not redirect unsafe methods./admin/follows Django admin's own login flow. Anonymous/studio/redirects once to/accounts/login/?next=%2Fstudio%2F; authenticated but unauthorized requests return403, not a misleading login loop.Scope
Development owner bootstrap and login
Implement one secret-safe bootstrap interface for an authorized operator. The preferred application contract is an idempotent
bootstrap_development_ownermanagement command/service that:DTC_ENVIRONMENT=development; test code may exercise the underlying service only with explicit test settings;accounts.CustomUserwhen absent, or resets only that same normalized identity after explicit reset confirmation; a conflicting existing identity or second owner attempt fails closed;site_admingroup for explicit Studio authority, and creates/updates the owner's linked humanAPIPrincipalwith only the credential-management and health permissions registered by this issue;core.access_studioinitially and no plaintext credential; the owner issues its first token in Studio;StaffSessionrows and owner-human API credentials before accepting a new login; it does not silently revoke or recreate the separately visible service credential;Document the exact authorized execution handoff without embedding a secret. If the deployed interactive execution mechanism is still under operational/AWS audit, engineering must still deliver and test this command/service interface; the operator invocation is
[HUMAN]and must not broaden CI/deployer AWS permissions or block the code deployment.On development only,
/accounts/login/adds a CSRF-protected local email/password form using the durable account backend. It uses generic invalid-credential responses and bounded throttling, preserves only a validated same-hostnext, rotates the Django session on success, and creates the existing revocableStaffSessionfor active staff. Production/local-test behavior does not silently enable routine local staff passwords.Explicit Studio authority
is_staff/is_superuserpermits Django admin as Django intends, but neither bypasses Studio authorization.StaffSession, and explicitsite_adminpermissions checked on every request.site_adminrole gains one explicit credential-management permission; lower roles receive none.Runtime credential workflows
Register non-test capabilities and readable CMP-based, line-broken Django templates for:
GET /studio/access/api-credentials/GET /api/v1/admin/credentialsPOST /studio/access/api-credentials/POST /api/v1/admin/credentialsPOST /studio/access/api-credentials/<uuid>/rotate/POST /api/v1/admin/credentials/<uuid>/rotatePOST /studio/access/api-credentials/<uuid>/revoke/POST /api/v1/admin/credentials/<uuid>/revokeAll adapters call shared
management_authquery/command services through the capability registry; no adapter writes models directly. Target querysets are limited to active service principals the actor may manage. Missing, malformed, out-of-scope, human, and unauthorized targets do not leak object existence.Create allows a safe name, an expiry within the existing 90-day maximum (default 30 days), and one or more non-test capability scopes that the target principal currently possesses. For this slice the development automation principal is entitled only to
studio.home.read, so its first token can callGET /api/v1/admin/health. No wildcard, future implicit scope, or unimplemented action is shown. Future registered capabilities become selectable only after their owning issue grants the service principal permission and the owner explicitly issues a new scoped credential.List responses contain only safe metadata: credential UUID, name, principal label/UUID, prefix, scopes, expiry, state, approximate last-used time, created time, and revision. They never expose the token, secret digest, password material, authorization header, or a recovery action.
Create and rotate reveal the raw token exactly once in the immediate successful response. Refresh, back navigation, idempotent replay, later GET/list, audit, database, logs, errors, OpenAPI examples, test artifacts, and screenshots cannot recover it. A create/rotate replay returns the existing safe result as
secret_unavailable_on_replaywithout a token. Rotation preserves approved scopes, defaults to zero overlap, permits only an explicitly requested overlap up to the existing one-hour maximum, and marks the predecessor unusable after that bound. Revocation is immediate and safely idempotent.Studio mutations require CSRF and explicit confirmation. Admin API create/rotate/revoke require Bearer authentication, the exact management scope/permission,
confirmed: true, andIdempotency-Key; rotate/revoke also require the current revision (If-Matchor the existing exact revision convention). Stale writes return409without mutation. Use existing bounded JSON, field allowlist, CORS-deny, rate, and safe error conventions.Every allowed and denied create/rotate/revoke and bootstrap/reset action writes redacted audit evidence with actor, target snapshot, outcome, request/idempotency correlation, scopes, expiry/state, and safe reason. Never audit credentials, passwords, digests, headers, cookies, session keys, email values, or request bodies containing secrets.
Management response policy
Every response and redirect under
/admin,/admin/,/studio,/studio/,/api/v1/admin/, and the development owner login flow isCache-Control: private, no-storeandX-Robots-Tag: noindex, nofollow; authenticated responses must not enter a public sitemap. Login, logout, denial, validation, one-time reveal, stale, and error states follow the same policy.Non-goals
/admin/, exposing Django admin models through the admin API, or restoring legacyaccounts.Token/plaintext CMP tokens.Acceptance criteria
site_admin, creates the human and development service principals, leaks no identity/secret, and refuses production, noninteractive secret input, conflicts, and a second owner.StaffSession, reaches both distinct/admin/and/studio/surfaces, and loses Studio access immediately after logout, reset, disablement, role removal, or session revocation./adminand/studioperform exact one-hop permanent safe-method redirects to their slash routes with query preservation; unsafe methods do not redirect; canonical surfaces remain distinct.studio.home.read, succeeds on/api/v1/admin/health, and is denied on missing scopes, credential-management operations, test-only operations, and unknown domain operations.[HUMAN]without secret evidence.Focused Django and security scenarios
next, duplicate normalized email, inactive/nonstaff/unassigned/superuser-only users, session fixation, logout, reset, revoke, and permission change. Studio never accepts flag-only authority./api/v1/admin/healthwith the first token, then prove missing/malformed/wrong/expired/revoked/rotated tokens and test-only/unregistered operations fail with the existing safe envelope. Regenerate OpenAPI and parity artifacts with only real runtime routes.Browser acceptance
At desktop (~1440×900) and mobile (~390×844), using isolated synthetic identities only:
/adminand/studio; verify direct slash redirects, distinct branding/purpose, correct login destination, no redirect loop, and private/noindex headers.next, Studio navigation, separate Django admin, responsive layout, visible focus, and logout/back-cache denial.Store screenshots only under
.tmp/screenshots/issue-107/, inspect each image, and post an independent tester report before PM acceptance.Dependencies and delivery
Closed #86, #87, and #100 are sufficient implementation prerequisites. #20/#28/#32/#33/#61 are production-policy follow-ups, not blockers for this development-only owner slice. #99's safe review-data boundary is evidence that production identities are absent, not a dependency and not permission to import them.
Follow
_docs/PROCESS.md: engineer implements/tests without committing; independent tester verifies and captures safe screenshots; PM accepts; engineer commits; orchestrator locally merges/pushes without a pull request; on-call alone observes CI/deployment. No secret may be used as lifecycle evidence.