DataTalksClub / DataTalksClub/website
Add self, accountless, Studio, and admin privacy-request surfaces
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 0
- Forks
- 0
- PR merge metrics
- No merged PRs in 30d
Description
Authoritative lifecycle hold — dependency reconciliation 2026-08-30
needs grooming + decision + human remain required; engineering and route registration are not authorized. Source-only #281/#283/#285 contracts feed #255/#257/#256, and #282 feeds #259; none authorizes a user or staff surface. PM may re-groom #260 only after accepted #255–#259 runtime services, every required production domain adapter, #32/#33 capability and high-risk controls, private artifact infrastructure, and the applicable HUMAN/legal values exist. Missing inputs remain private 404/fail-closed, not partial UI.
Parent epic: #64
Privacy foundations: #254, #255, #256, #257, #258, #259
Account/domain adapters: #251 and separately accepted adapters for every required #254 manifest domain
Staff/admin dependencies: #32 and #33
Email/processor and restore dependencies: #49 and #66 where applicable
Product outcome
People with a verified durable account and people using an approved accountless proof can submit and follow one privacy request, download their own available portable export, submit accepted corrections, and see safe progress or failure states. Authorized staff can find, inspect, and advance the same aggregate through Studio and /api/v1/admin/ adapters. Every adapter calls only the shared privacy services accepted in #255-#259 and each owning domain's accepted service; presentation code never queries or mutates domain data directly.
This issue is not engineer-ready. It retains needs grooming, decision, and human. The route, HTTP, identifier, response, concurrency, caching, masking, and verification envelope below is reserved and exact, but every route remains unregistered and absent from navigation/OpenAPI until the activation gates are accepted. Runtime absence must fail closed as the normal private 404; no placeholder form, development-only bypass, fake verifier, or partially functional staff route may ship.
Normative authority
_docs/PROCESS.md: role-separated grooming, engineering, independent testing/screenshots, PM acceptance, and HUMAN evidence._docs/specs/01-platform-architecture.mdand_docs/architecture/app-boundaries.md: one deployment; public/self/Studio/API are adapters; owning application services alone mutate domain data; network work occurs after commit through durable jobs._docs/specs/06-studio-and-admin-api.md: exact management parity, capability/queryset/field authorization, revision/idempotency, audit, pagination, OpenAPI, private caching, and high-risk controls._docs/specs/07-security-privacy-operations.md: rights for account and accountless subjects, full-erasure direction, export/correction/propagation/restore requirements, redaction, accessibility, and approved #23 defaults._docs/specs/10-verification-strategy.md: authorization, stale/replay, privacy, accessibility, browser/history/cache, processor, and restore evidence.- Closed #23, parent #64, and #254: approved privacy contact/defaults and the remaining HUMAN legal/accountless decisions.
- Accepted #255-#259 contracts when available: aggregate/proof/domain work, export/correction, retention, erasure/restore fencing, invalidation, and processor receipt state. This issue cannot weaken or duplicate them.
Activation and dependency gates
No route below may be registered until all service/model contracts it exposes are accepted, merged, and integrated. At minimum:
- #254 supplies one accepted policy/notice/manifest identity and exact requester-safe wording/projection rows.
- #255 supplies the aggregate, proof-verifier interface, rights, lifecycle, revisions, blocks, domain work, operation and authorization seams.
- #256 supplies private export/download and correction DTOs/services plus accepted private artifact infrastructure and every required production domain adapter.
- Any surfaced retention, erasure, restore, invalidation, or processor state depends on accepted #257, #258, #259, #49, #66, and the relevant production adapters. Missing/unknown state is
blocked, never hidden or treated as complete. - Account/profile/accountless/domain adapters, including #251 where applicable, must bind an opaque subject to the exact authorized requester without a runtime cross-domain import or presentation-layer PII lookup.
- Production Studio/admin registration depends on accepted #32/#33 staff identity, effective-capability, object/field authorization, masked/full-PII, reauthentication/API-equivalent proof, confirmation, audit, API credential, rate, and OpenAPI contracts. No group, including
site_admin, receives privacy capabilities by inference. - The HUMAN values listed below must be accepted as a versioned #254 authority update. Until then, accountless intake/proof/recovery and restriction/objection/appeal/escalation actions are absent. Any other surface whose copy, proof, deadline, or high-risk behavior depends on one of those values is also absent.
Activation is all-or-nothing per declared capability family. Route registry, capability registry, OpenAPI, navigation, route-cache classification, sitemap/search exclusions, templates, and tests must agree. A missing dependency, verifier, adapter, artifact store, capability policy, notice version, deadline policy, or safe projection prevents registration at startup/system check time.
Reserved requester routes and methods
All UUID path values are opaque public UUIDs issued by the privacy service. They are locators, not credentials. Raw proof, email, name, account ID, provider ID, object key, domain record ID, token, or reversible subject digest is never accepted in a URL/query string.
Authenticated account HTML
| Method | Route | Purpose |
|---|---|---|
GET |
/accounts/privacy/requests/ |
Owner-scoped list, empty state, and request intake form. |
POST |
/accounts/privacy/requests/ |
Create one request with an explicit set of accepted rights and the active notice acknowledgement. |
GET |
/accounts/privacy/requests/<uuid:request_id>/ |
Owner-scoped status, per-domain safe progress, blocks, deadlines, operations, and available actions. |
POST |
/accounts/privacy/requests/<uuid:request_id>/cancel/ |
Cancel only through #255's allowed transition. |
POST |
/accounts/privacy/requests/<uuid:request_id>/resume/ |
Recheck every invariant and request an allowed resume. |
POST |
/accounts/privacy/requests/<uuid:request_id>/exports/ |
Start a new portable-export generation through #256. |
POST |
/accounts/privacy/requests/<uuid:request_id>/exports/<uuid:export_id>/download/ |
Authorize and stream one attempt through #256; never redirect to storage. |
POST |
/accounts/privacy/requests/<uuid:request_id>/corrections/ |
Submit only schema-declared, export-local correction commands through #256. |
There are no GET mutations, query-string actions, bulk subject actions, arbitrary domain selectors, requester hold/exception overrides, or self-service deadline/appeal decisions.
Session-authenticated self API
The learner/self schema reserves:
GET/POST /api/v1/me/privacy-requests—listMyPrivacyRequests/createMyPrivacyRequest;GET /api/v1/me/privacy-requests/{request_id}—getMyPrivacyRequest;POST /api/v1/me/privacy-requests/{request_id}/cancel—cancelMyPrivacyRequest;POST /api/v1/me/privacy-requests/{request_id}/resume—resumeMyPrivacyRequest;POST /api/v1/me/privacy-requests/{request_id}/exports—createMyPrivacyExport;POST /api/v1/me/privacy-requests/{request_id}/exports/{export_id}/download—downloadMyPrivacyExport, returning onlyapplication/zipon success;POST /api/v1/me/privacy-requests/{request_id}/corrections—createMyPrivacyCorrection.
This API accepts the normal secure session only, never a Studio/admin bearer principal. Every unsafe request requires CSRF. The binary download is the sole non-JSON success response; errors retain the self-API safe error envelope. HTML and self API share services and outcomes, not HTTP self-calls.
Accountless HTML
The following routes are reserved but disabled pending the HUMAN accountless-proof/recovery contract:
GET/POST /privacy/requests/— accessible intake and uniform submission response;POST /privacy/requests/verify/— proof submitted only in a CSRF-protected request body;GET /privacy/requests/<uuid:request_id>/— proof/session-bound non-enumerating status;- the same
cancel,resume,exports,exports/<export_id>/download, andcorrectionsPOST suffixes as the authenticated HTML routes.
There is no accountless JSON API in this issue. The eventual verifier must issue a purpose-bound, expiring, one-request browser binding through an HttpOnly, Secure, SameSite cookie and #255 assertion; exact email/OTP/link/recovery delivery, assurance, lifetime, retry, binding, account-merge behavior, and requester evidence remain HUMAN-required. A proof token is never put in a URL, Referer, title, DOM after consumption, cookie readable by JavaScript, database request field, log, audit, screenshot, analytics event, or OpenAPI example. Invalid, missing, expired, replayed, revoked, merged, disabled, quarantined, or unavailable proof uses the same response shape and timing class and reveals neither subject nor request existence.
Reserved Studio and admin API parity
Studio
GET /studio/privacy/requests/— bounded authorized list/filter.GET /studio/privacy/requests/<uuid:request_id>/— authorized masked detail and safe per-domain/operation/receipt progress.- confirmed POST actions on the detail route suffixes:
exports/,corrections/,restriction/,objection/,erasure/,resume/, andcancel/. POST /studio/privacy/requests/<uuid:request_id>/exports/<uuid:export_id>/download/— separately authorized, confirmed one-subject export stream.
Admin API
| Method and route | Operation ID | Capability key |
|---|---|---|
GET /api/v1/admin/privacy-requests |
listPrivacyRequests |
privacy.request.view |
GET /api/v1/admin/privacy-requests/{request_id} |
getPrivacyRequest |
privacy.request.view |
POST /api/v1/admin/privacy-requests/{request_id}/exports |
createPrivacyExport |
privacy.request.export |
POST /api/v1/admin/privacy-requests/{request_id}/exports/{export_id}/download |
downloadPrivacyExport |
privacy.request.export |
POST /api/v1/admin/privacy-requests/{request_id}/corrections |
createPrivacyCorrection |
privacy.request.correct |
POST /api/v1/admin/privacy-requests/{request_id}/restriction |
applyPrivacyRestriction |
privacy.request.restrict |
POST /api/v1/admin/privacy-requests/{request_id}/objection |
resolvePrivacyObjection |
privacy.request.object |
POST /api/v1/admin/privacy-requests/{request_id}/erasure |
startPrivacyErasure |
privacy.request.erase |
POST /api/v1/admin/privacy-requests/{request_id}/resume |
resumePrivacyRequest |
privacy.request.resume |
POST /api/v1/admin/privacy-requests/{request_id}/cancel |
cancelPrivacyRequest |
privacy.request.cancel |
privacy.request.view_pii is a sensitive-field capability, never a route-level substitute for an action capability. It permits only fields explicitly authorized by the active #254 projection and the owning adapter; the kernel itself contains no PII. Without it, staff see only opaque request/export/operation IDs, subject class, rights, lifecycle/revision, bounded timestamps, domain keys, numeric counts, digests where approved, and allowlisted safe codes. Lists cannot filter or sort on contact/identity values and accept only status, subject_class, right, deadline_state, received_after, and received_before; sorts are received_at, -received_at, updated_at, and -updated_at. Authorization filters the queryset before lookup, and denied/cross-object list, detail, action, and download paths never reveal existence.
No role-to-capability mapping is chosen here. #32/#33 must explicitly map each capability, view_pii, and API scope and must pin reauthentication/API-equivalent proof. PII view/export, correction, restriction/objection disposition, erasure, resume after a legal block, and cancellation after work begins are high-risk and remain runtime-disabled until that accepted mapping and confirmation policy exist.
Request, limits, revision, idempotency, and confirmation contract
- HTML and self API operate on the currently revalidated opaque subject binding. Studio/admin API operate on the capability-filtered queryset. Account merge, disablement, quarantine, deletion, proof revocation, or binding drift rechecks through the accepted verifier on every detail/action/download and fails closed.
- HTML POST and self API POST require CSRF. Studio POST requires the OIDC staff session, active-staff recheck, CSRF, and #32 freshness where classified high-risk. Admin API uses Bearer authentication only, deny-by-default CORS, and the #33 API-equivalent high-risk evidence; it is CSRF-exempt only because cookies are not accepted.
- Every HTML form mutation contains server-issued
expected_revision,idempotency_key, and the exact allowed fields. Admin/self API mutations require a strongIf-Match: "rev-N"for an existing mutable request/export/batch andIdempotency-Keyof 1-512 UTF-8 bytes. MissingIf-Matchis428; malformed input is400; stale revision or changed-key replay is409. Exact replay returns the original bounded safe result and never consumes another download attempt or starts another operation. - JSON is UTF-8
application/json, at most 65,536 bytes, depth 12, 2,000 nodes, and key length 128. HTML form bodies use the same 65,536-byte application limit. Unknown/repeated fields, duplicate JSON keys, noncanonical values, floats/NaN, unsafe Unicode/control data, free-form evidence, and mass assignment are rejected. Correction and export content remain subject to #256's stricter field/archive/record/byte/time bounds. There is no bulk subject endpoint; management page size defaults to 20 and is bounded 1-100. - Admin API/Studio use the existing 60-second principal window: 120 read credits and 30 write credits; list/detail cost 1, export/correction/download cost 5, and restriction/objection/erasure/resume/cancel cost 10. Rate-store failure denies the request. Exact self and accountless per-subject/safe-IP limits remain a required HUMAN/abuse decision; those routes stay disabled until recorded. Edge IP limits are additional and never authorization.
- Create accepts an exact nonempty set of #255 rights and the active
notice_version; rights become immutable afterpending_proof. Correction accepts only #256 definitions and allowlisted reason codes. Staff high-risk requests require body fieldconfirmed: true, an allowlistedreason_codeof 1-64 lowercase ASCII characters, current revision, idempotency key, and the exact accepted #32/#33 freshness/API proof. Requester erasure/export/correction confirmation copy and whether reauthentication is required remain HUMAN legal/security values; affected actions stay absent until accepted. - Long-running export/correction/retention/erasure/propagation work returns
202with the existing safecore.Operationresource. Creation may return201only after atomic aggregate creation. Successful reads are200; authenticated cancellation with no queued work may return200. No response claims completion while any required domain, deletion, processor receipt, or restore fence is pending, partial, ambiguous, unknown, or blocked.
Status, result, error, and OpenAPI contract
Requester and staff status use the canonical #255 request states only: pending_proof, ready, processing, blocked, completed, cancelled; deadline state is unresolved|resolved|overdue. Export/correction/retention/restore/processor child state is shown only from accepted safe DTOs. partial is visibly nonterminal, provider_accepted is not delivery, and unknown/ambiguous never becomes success. User copy explains the next safe action without exposing an adapter, hold, exception, processor, other person, or staff-only security detail.
Admin API errors preserve the repository envelope:
{"error":{"code":"safe_code","message":"Code-owned generic text.","request_id":"opaque-request-id","fields":{"field":["safe_code"]}}}
Self API uses the same bounded structure with self-schema documentation. Only code-owned allowlisted messages/codes are serialized. 401 is unauthenticated, 403 is a capability denial with no object lookup, 404 is the uniform post-queryset missing/cross-object result, 400 is malformed/invalid fields, 409 is stale or idempotency/state conflict, 413 is oversized, 415 is wrong media type, 428 is missing precondition, 429 is throttled with bounded Retry-After, and 503 is a fail-closed dependency/proof/store/control outage. No stack, email, name, raw proof, field value, domain locator, object key/URL, provider response, hold/exception prose, or another subject's existence appears.
Generated OpenAPI 3.1 must include every registered self/admin route, exact operation ID, auth scheme, capability/scope extension, parameters, If-Match/Idempotency-Key, body/result/error schemas, rate and size bounds, binary download media/headers, status codes, examples, and runtime info.version. CI proves route/capability/service/audit/idempotency/result parity and Studio equivalence. Accountless HTML routes do not enter OpenAPI. Examples use only synthetic opaque UUIDs, states, counts, and .invalid values where a HUMAN-approved contact example is unavoidable; never a token, proof, PII, object key, provider payload, or production identifier.
Private-cache, browser-history, and security contract
Every self, accountless, Studio, admin, export, error, redirect, and authentication/proof response is zero-TTL and emits Cache-Control: private, no-store, X-Robots-Tag: noindex, nofollow, Referrer-Policy: no-referrer, and the applicable CSP/security headers. Authenticated responses vary safely on the relevant cookie or Authorization; CloudFront never stores them. Routes are absent from sitemap, search, public serializers, analytics payloads, public caches, and canonical/alternate link output.
Page titles, headings, URLs, query strings, flash messages, DOM data attributes, download filenames, browser history, console/network logs, metrics, traces, audits, operations, jobs, exceptions, screenshots, and issue evidence contain no name, email, raw/reversible subject value, proof/token, domain record ID, correction value, artifact locator, provider body, Slack URL, or staff-only note. Download responses follow #256 exactly: reauthorize each attempt, maximum three stream starts, 24-hour availability, application/zip, safe attachment name containing only opaque IDs, nosniff, no Range, no presigned redirect, and application-enforced expiry even if deletion lags.
Forms use semantic landmarks/headings, explicit labels/instructions, error summary linked to fields, preserved nonsensitive input, visible focus, keyboard completion, 44px targets, live-region status, human and machine-readable UTC dates, 200% zoom/reflow, reduced-motion support, and no color-only state. Back/forward, refresh, duplicate submit, logout/login, two tabs, and another browser never reveal cached content or duplicate a command. Logout, session expiry, proof expiry, account quarantine/disablement, staff revocation, and credential revocation deny the next read/action immediately within the accepted identity-control bound.
Browser and verification matrix
After activation, the independent tester uses only synthetic .invalid identities and captures/inspects desktop approximately 1440x900 and mobile approximately 390x844 under .tmp/screenshots/issue-260/:
- Authenticated owner: empty list, intake, validation/focus/preserved safe fields, exact replay, status/detail, one/many domains, pending/ready/processing/blocked/partial/completed/cancelled/overdue, refresh/back/two-tab stale conflict, and another account's UUID.
- Accountless: uniform intake, valid/missing/expired/replayed/revoked/wrong-browser proof, recovery once approved, safe status, merge/absorption/disable/quarantine, cross-request UUID, throttling, and no enumeration/timing/body difference beyond accepted bounds.
- Export: generation, unavailable/available/expired/deleting/deleted/blocked, three-start boundary, interrupted stream, changed proof/revision, missing object, no Range/presigned redirect, safe filename/headers, logout/back/history, and no bytes or locator in screenshot/DOM/log.
- Correction/restriction/objection/erasure: allowed fields, hidden-field/mass-assignment/cross-domain denial, confirmation/freshness, stale revision, same/changed idempotency replay, partial/resume, active hold/exception, ambiguous processor/restore state, and no direct domain mutation.
- Studio/admin: each capability allowed and denied, masked versus explicitly authorized projection, queryset-before-lookup, no PII search, staff/API revocation, confirmation/reason/freshness, rate/body limits, exact Studio/API service/result/audit parity, and OpenAPI drift.
- Security/accessibility: CSRF, cookie flags, CORS denial, private/no-store/noindex/referrer/cache warm/bypass, browser history, title/URL/console/network/audit canaries, keyboard, screen reader announcements, focus, contrast, 200% zoom/reflow, reduced motion, and JavaScript-disabled progressive completion where supported.
Screenshots contain opaque IDs and safe states only, show the intended page rather than an error/debug page, and never include proof, token, email, name, correction value, export bytes/key, provider data, or production information. Any registered rendered surface makes Playwright and inspected screenshots required; they are not not_applicable for this issue.
Repository verification additionally covers route/capability/OpenAPI parity, both databases' concurrency/constraints, CSRF/session/token/rate limits, cache classifier and sitemap/search exclusion, service-only mutation/import boundaries, stale/replay/crash/reconcile, audit/log/metric canaries, #256 storage cleanup, #259 processor ambiguity, and #258/#66 restore fencing through the graph-selected versioned plan.
Remaining HUMAN/legal contract
An authorized privacy/legal owner must record a versioned #254 authority update for all of the following before affected routes activate:
- controller/operator identity/address/representative, final Privacy/Terms/Impressum/right/contact/complaint/supervisory-authority wording, effective date, governing law/venue where applicable, and the already approved privacy contact's exact displayed use;
- exact purpose/lawful basis, notice/consent version, requester acknowledgement, restriction and objection meanings/outcomes, withdrawal behavior, hold/exception disclosure, issued-certificate exception wording, and safe requester result/omission evidence;
- accountless proof channel, assurance, expiry, recovery, replay/browser binding, absorption into an account, lost-access handling, abuse throttles, escalation/appeal proof, and whether any contact receipt may be retained;
- receipt/acknowledgement, response, fulfilment, appeal, escalation, restriction, objection, processor, and deletion deadlines plus overdue behavior and competent-contact wording;
- requester confirmation and reauthentication requirements for export, correction, restriction, objection, and erasure; staff/API high-risk behavior must also match accepted #32/#33;
- processor confirmation/receipt, retry/escalation, suppression-versus-erasure, tombstone/receipt retention, legal/security/abuse holds, educational-record end trigger, production log period, and retained non-PII proof.
[HUMAN] In an authorized synthetic production-like environment, the legal/privacy reviewer verifies the exact displayed legal/contact/rights/deadline/complaint/effective-date copy and completes account and accountless intake, proof, status, download, correction, restriction/objection, and erasure/restore/processor-failure journeys. Evidence is redacted, contains no real requester/provider data or credential, and cannot substitute for independent tester and PM gates.
Explicit non-goals
- No legal advice or inferred policy; no real/protected requester lookup, request, export, correction, retention, erasure, processor contact, restore, or production rehearsal during ordinary engineering/testing.
- No new kernel/export/correction/retention/tombstone/processor/domain behavior; no domain model import or mutation from views; no accounts-to-courses dependency; no cascade deletion.
- No direct Relay/SES/Datamailer/provider/storage fallback; no presigned/public artifact URL; no email attachment; no raw token in any URL.
- No bulk subject export/action, unrestricted staff impersonation, Django-admin substitute, public requester directory, PII search without an accepted protected lookup service, or generic anonymize-and-retain escape hatch.
- No route, navigation, OpenAPI operation, fake proof, placeholder legal copy, default deadline, role grant, high-risk bypass, production adapter, or partial activation while a named dependency/HUMAN value is unresolved.
Acceptance and delivery convention
- Every dependency and HUMAN value above is accepted in its owning issue/version; route/capability/OpenAPI registration fails closed on drift or absence.
- Exact routes, methods, authorization, CSRF, limits, revisions, idempotency, confirmation, statuses/errors, capabilities/masking, private-cache/history, accessibility, and service parity match this contract.
- Every action calls only the accepted shared privacy/owning-domain service and produces bounded redacted audit/operation evidence; no direct domain mutation or network side effect exists.
- Automated, database, security, OpenAPI/parity, browser, screenshot, and HUMAN evidence all pass through separate engineer, tester, PM, and authorized-review gates.
Keep needs grooming, decision, and human; do not dispatch engineering. Once the blockers are resolved, PM must reconcile their exact accepted values into this body, remove needs grooming, and only then hand off. Final commit uses Refs #260 while any HUMAN production check remains; Closes #260 is allowed only after all automated, PM, HUMAN, deployment, and on-call gates pass.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with _docs/PROCESS.md, _docs/specs/01-platform-architecture.md, _docs/architecture/app-boundaries.md, and _docs/specs/06-studio-and-admin-api.md. Review the accepted contracts in #254–#259 and the #32/#33 and HUMAN gates first; this issue is done only when those dependencies are accepted and all declared route, capability, OpenAPI, navigation, and fail-closed checks agree.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- api, authentication, authorization, backend, security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 10/100