Privacy Center — Improvement & Growth Plan
Package: capell-app/privacy-center · Kind: package · Tier: premium · Product group: Capell Operations · Bundle: operations · Status: Draft
1. Snapshot
Section titled “1. Snapshot”Privacy Center is an admin + console package providing a package-owned compliance ledger: consent records, policy versions/acceptances, subject (DSAR) requests, retention rules, plus export/anonymize Actions. It ships 5 models/tables (privacy_consent_policies, privacy_consent_records, privacy_policy_acceptances, privacy_retention_rules, privacy_requests), 11 Actions (src/Actions), 5 Filament resources + 1 stats widget (src/Filament), 8 Data objects, 8 enums, and a per-subject SHA-256 hashing helper (src/Support/PrivacyIdentifier.php). Surfaces declared: admin, console. Deps: requires capell-app/admin + capell-app/core; supports access-gate, contacts, insights, newsletter. The only live cross-package integration today is insights calling RecordConsentAction via packages/insights/src/Actions/MirrorInsightsConsentToPrivacyCenterAction.php.
Current marketplace summary (verbatim): “Privacy Center provides the compliance workflow foundation for consent, retention, privacy requests, exports, deletion, and policy acceptance.” Screenshot count: 0 (marketplace.screenshots: []) — an optional docs/screenshots.json contract now documents the intended request queue, workflow action, retention-rule, and dashboard-widget captures, but the current screenshot runner app does not require capell-app/privacy-center, so valid package routes cannot be captured until that runner requirement/install gap is closed. README.md and CHANGELOG.md are absent from disk (only docs/overview.md exists), despite composer.json advertising a docs URL.
2. Improvements (existing functionality)
Section titled “2. Improvements (existing functionality)”- Shipped 2026-06-05: DSAR edit workflow actions stamp request state through Actions.
PrivacyRequestResourceexposes translated “Mark verified”, “Mark fulfilled”, and “Reject” actions that delegate toMarkPrivacyRequestVerifiedAction,MarkPrivacyRequestFulfilledAction, andRejectPrivacyRequestAction. Evidence:PrivacyCenterAdminSurfaceTestnow exercises the edit-page action objects and provesverified_at,fulfilled_at,rejected_at, andrejection_reasonare written. —src/Filament/Resources/PrivacyRequests/PrivacyRequestResource.php,tests/Unit/PrivacyCenterAdminSurfaceTest.php - Shipped 2026-06-06: retention rules can be dry-run or executed from admin.
RetentionRuleResourcenow exposes translated per-rowDry runandRun nowactions. Dry-run delegates toApplyRetentionRuleActionwithdryRun: trueand reports matched records without mutating data; run-now applies the rule after confirmation and reports matched/affected counts through Filament notifications. —src/Actions/ApplyRetentionRuleAction.php,src/Filament/Resources/RetentionRules/RetentionRuleResource.php - Shipped 2026-06-06: record table filters added.
ConsentRecordResourcenow filters by category, decision, and jurisdiction;PrivacyRequestResourcefilters by type, status, and overdue due dates;PolicyAcceptanceResourcefilters by policy type and context. —src/Filament/Resources/*/*Resource.php— S - Index/guard against orphaned
policy_idafter retention anonymize —ApplyRetentionRuleAction::anonymize()andAnonymizePrivacySubjectActionnull out subject/evidence but leavepolicy_id; fine, butRegisterConsentPolicyAction+ConsentPolicydeletion usesnullOnDelete— confirm anonymized rows remain queryable bycategory/decided_at(they do via existing indexes). Low-effort: add an explicit “anonymized” marker inmetadataso audit reports can distinguish anonymized vs never-linked rows. —src/Actions/ApplyRetentionRuleAction.php— S - Shipped 2026-06-05:
PrivacyIdentifier::hashSecret()fails loudly instead of using a hard-coded literal salt. If neitherCAPELL_PRIVACY_CENTER_HASH_SECRETnorapp.keyis set,PrivacyIdentifiernow throws before hashing consent evidence. Evidence:src/Support/PrivacyIdentifier.php;tests/Unit/PrivacyIdentifierTest.php. — S - Shipped 2026-06-06: overview widget stats are cached behind the
privacy-centertag.BuildPrivacyCenterOverviewStatsActionnow wraps the four dashboard counts inPrivacyCenterOverviewStatsCache, with a configurable 300-second TTL and tag fallback handling.ConsentRecord,PrivacyRequest, andRetentionRulemodel events flush the cached stats when records affecting the widget are saved or deleted. —src/Actions/BuildPrivacyCenterOverviewStatsAction.php,src/Support/PrivacyCenterOverviewStatsCache.php,src/Models/* KeyValueediting ofworkflow_payload/metadataon the request form is free-text and unvalidated — operators can corrupt structured workflow payloads. Make these read-only or render via a structured infolist. —src/Filament/Resources/PrivacyRequests/PrivacyRequestResource.php— S
3. Missing Features (gaps)
Section titled “3. Missing Features (gaps)”Mapped to capabilities[]:
- Done/Shipped: public cookie-category consent surface. Privacy Center now ships
/privacy/consent, a public preference center, plus an embeddable banner partial. The form submits category handles only, grants essential cookies automatically, records optional categories as granted/denied throughRecordConsentAction, sets a saved-preferences cookie, and keeps policy model IDs, admin URLs, hashed evidence, package internals, and editor state out of anonymous HTML.capell.jsonnow restoresprivacy-center-cookie-categories, declares a public surface, and sets a 40ms frontend render budget for the cache-safe public preference center. —routes/web.php,src/Http/Controllers/*ConsentPreferencesController.php,resources/views/consent/*,tests/Feature/PublicConsentPreferenceCenterTest.php - Shipped 2026-06-05:
privacy-center-retention-executionis now reachable.ApplyRetentionRulesActionis exposed throughprivacy:apply-retention,PrivacyRetentionScheduleContributiondeclares the command and daily frequency incapell.json, andPrivacyCenterServiceProviderschedules the command daily. Evidence:src/Console/Commands/ApplyRetentionRulesCommand.php;src/Providers/PrivacyCenterServiceProvider.php;capell.json. - No public DSAR intake (subject request) form.
OpenPrivacyRequestActionexists, but onlyinsights/code can call it. GDPR Art. 12/15-22 + CCPA require a consumer-facing way to submit access/export/delete requests. No public route, no verification (email confirmation) flow —verified_atexists on the model but nothing populates it. — table-stakes. - DSAR export/erasure is single-package only (completeness gap).
BuildPrivacyExportActionandAnonymizePrivacySubjectActiononly touch Privacy Center’s own 3 tables. There is no cross-package erasure/export registry — no contract a package implements to contribute its data to a subject export or to be erased.docs/overview.mdexplicitly defers this (“operate on Privacy Center records first”), but a “Privacy Center” that can’t fulfil a real right-to-erasure acrosscontacts/insights/newsletteris incomplete. — key differentiator if built: aContributesSubjectData/ErasesSubjectDatacontract that other packages register against. - Shipped 2026-06-05: Insights-mirrored consent now carries a subject link.
RecordConsentActionnow infers a loadedsubjectorvisitmodel from the source when no explicit subject is passed, matching the existing Insights mirror call shape where the consent source is loaded withvisit. Mirrored records now receivesubject_type/subject_id, soBuildPrivacyExportActionandAnonymizePrivacySubjectActioncan find them. Evidence:src/Actions/RecordConsentAction.php;tests/Feature/PrivacyCenterFoundationTest.php. - No consent proof export / certificate. Records exist but there’s no “produce signed proof of consent at time T” output (CSV/PDF), which is the artefact regulators/auditors actually ask for. — differentiator.
- No geo/jurisdiction-aware policy selection.
jurisdictionis a free string column on consent; nothing maps a visitor region → which policy/categories apply (GDPR vs CCPA vs nothing). — differentiator. - No consent expiry/refresh job.
expires_atexists onConsentRecordandConsentDecision::Expiredexists, but nothing transitions granted→expired or re-prompts. — table-stakes for “valid consent” claims. - No policy publication workflow.
ConsentPolicyhaspublished_at/effective_at/retired_atbut the resource is plain CRUD — no “publish”/“retire” action, no enforcement that acceptances reference a published version.
4. Issues / Risks
Section titled “4. Issues / Risks”- Shipped 2026-06-05: Manifest capability overclaims reconciled for the obvious public-surface gap.
privacy-center-retention-executionnow has aprivacy:apply-retentioncommand and daily scheduled-job metadata;privacy-center-cookie-categorieswas removed fromcapell.jsonuntil a public banner/preference centre ships. Evidence:capell.json;src/Console/Commands/ApplyRetentionRulesCommand.php;tests/Unit/ManifestRequirementsTest.php. - Shipped 2026-06-05: Health check now verifies real package discoverability.
PrivacyCenterHealthCheckverifies required storage tables, morph-map model aliases, the loaded service provider, and identity hash configuration. Evidence:src/Health/PrivacyCenterHealthCheck.php;tests/Unit/PrivacyCenterHealthCheckTest.php. - All four
Manifest/*Contributionclasses are behaviourless markers.ConsentPolicyResourceContribution,PrivacyCenterOverviewWidgetContribution,PrivacyCenterModelsContribution,PrivacyRetentionScheduleContributiononly return a version string. They passManifestRequirementsTest(which asserts the marker interface is implemented) but encode no real contribution beyond whatcapell.jsonstrings declare. Acceptable per core’s design, but it means the test suite proves declaration, not behaviour. - Test gaps:
- No test exercises the scheduled job actually running (because nothing wires it). The retention tests call
ApplyRetentionRulesAction::run()directly — they would pass even though production never triggers it. - Shipped 2026-06-05: focused Filament edit action-object coverage proves verifying, fulfilling, and rejecting privacy requests stamps the expected workflow fields. Full Livewire page render/save interaction coverage remains open.
- Shipped 2026-06-05:
PrivacyIdentifierhash determinism, secret variance, and fail-loud missing-secret behavior are covered intests/Unit/PrivacyIdentifierTest.php. Anonymization irreversibility still needs direct coverage. - No public-output cache-safety test, despite the skill mandating one for any package with public surface — currently moot because there is no public surface, but the moment a banner ships this becomes required.
- No assertion that the insights mirror produces records discoverable by export (it doesn’t — see §3).
- No test exercises the scheduled job actually running (because nothing wires it). The retention tests call
- Consent-record integrity / proof. Only
ip_hash/user_agent_hash+ free-formevidenceJSON are stored. There is no immutable/append-only guarantee and no tamper-evidence (e.g. hash chaining), so the ledger’s value as legal proof is weak.evidenceis nulled on anonymize, which is correct, but there’s no retained “consent happened” skeleton record after subject erasure. - Cache-safety posture is declared but untested.
performance.cacheSafetycorrectly setscacheable:false, sensitiveOutput:true, variesBy:[site,subject]. Good. But the admin tables/widget pull hashed identifiers (email_hash,ip_hash) into the UI; ensure these never leak to non-admin/log surfaces.adminQueryBudget: 40is declared but nothing measures it; the widget’s 4 counts + 5 resource list queries could exceed it on heavy pages. frontendRenderBudgetMs: 0correctly signals “no frontend render” — but it will need a real budget the moment a consent banner is added; flag now.- i18n. Enum labels and admin strings are fully translated (
resources/lang/en/privacy.php) — good. But there is exactly one locale (en), and the (future) public banner/DSAR form copy isn’t covered at all. - Docs.
README.mdandCHANGELOG.mddo not exist on disk;composer.jsonadvertisesdocs: https://docs.capell.app/packages/privacy-center. Onlydocs/overview.md(which is candid that admin/retention surfaces are deferred). For a paid package this is thin.
5. Marketplace & Selling
Section titled “5. Marketplace & Selling”Critique. The current summary (“…compliance workflow foundation for consent, retention, privacy requests, exports, deletion, and policy acceptance”) and composer description (“Privacy, consent, retention, and subject request foundation for Capell CMS”) both lean on the word “foundation,” which is honest (it is a ledger of Actions) but undersells nothing and oversells the cookie-banner/retention-execution capabilities that aren’t reachable. Buyers reading “consent” + “cookie categories” will expect a banner; there is none. Zero screenshots for a premium paid SKU is a conversion killer.
Improved 1-sentence summary:
The compliance backbone for Capell — one auditable ledger for cookie consent, policy acceptances, retention rules, and GDPR/CCPA subject requests, with Actions your other packages plug straight into.
Improved 3–4 sentence description:
Privacy Center gives every Capell site a single, queryable system of record for privacy obligations: granular cookie-category consent, versioned policy acceptances, retention rules, and access/export/delete subject requests. Other Capell packages — Insights, Contacts, Newsletter — record consent and contribute subject data through stable Actions, so exports and erasure stay complete across the stack. Hashed request evidence (IP, user-agent) and immutable consent records give you defensible proof, while scheduled retention keeps data minimised automatically. Admin operators get resources and an at-a-glance compliance dashboard; nothing sensitive ever leaks to public output.
(Note: the second and third sentences describe the target state — the cross-package contract and scheduled retention are gaps in §3 and must ship before this copy is truthful.)
Screenshot/media gaps: need at minimum (1) the privacy-requests queue with status badges, (2) retention rules list, (3) the overview dashboard widget, (4) a request detail with fulfil/reject actions, and — once built — (5) the public cookie banner / preference centre. Currently screenshots: []; the runner contract is present but blocked until the runner app requires capell-app/privacy-center.
Pricing/tier/bundle positioning. Premium tier in the operations bundle is right — privacy is a horizontal, regulatory must-have, not a nice-to-have, and bundling with other Operations packages suits agencies/regulated buyers. The strongest commercial wedge is the Extension Suite cross-sell: it supports insights, contacts, newsletter, access-gate. Lead with “buy Insights and Privacy Center so analytics consent is captured and erasable,” and “Contacts + Privacy Center = real right-to-erasure for your CRM.” The inbound insights mirror already demonstrates the pattern; make the outbound export/erasure contract the headline suite feature.
Differentiators / value props / target buyer. Target buyer: agencies and in-house teams running EU/UK/CA-facing Capell sites who need defensible compliance without bolting on a third-party CMP. Value props: (1) consent + DSAR live inside the CMS, not a separate SaaS; (2) one ledger across all Capell packages; (3) auditable, hashed proof; (4) automated retention. Differentiator vs table-stakes: a generic cookie banner is table-stakes (and currently missing); the cross-package subject-data contract (one erasure call wipes contacts + analytics + newsletter consent) is the genuine differentiator no standalone CMP offers.
Keywords/tags (8–12): gdpr, ccpa, cookie-consent, consent-management, dsar, subject-access-request, right-to-erasure, data-retention, privacy, compliance, audit-log, policy-acceptance.
6. Prioritized Roadmap
Section titled “6. Prioritized Roadmap”| Item | Bucket | Effort | Impact | Section ref |
|---|---|---|---|---|
Shipped 2026-06-05: Wire retention execution with privacy:apply-retention console command + command/frequency in capell.json scheduled-job block. Evidence: src/Console/Commands/ApplyRetentionRulesCommand.php, src/Providers/PrivacyCenterServiceProvider.php, capell.json | Done | S | High | §3, §4 |
Shipped 2026-06-05: Make PrivacyCenterHealthCheck verify tables/models/provider discoverable. Evidence: src/Health/PrivacyCenterHealthCheck.php, tests/Unit/PrivacyCenterHealthCheckTest.php | Done | S | High | §4 |
Shipped 2026-06-05: Fix insights mirror subject resolution so mirrored consent is exportable/erasable. Evidence: src/Actions/RecordConsentAction.php; tests/Feature/PrivacyCenterFoundationTest.php | Done | S | High | §3 |
Shipped 2026-06-05: Add fulfil/verify/reject UI actions on request edit page (delegate to Actions, stamp timestamps). Evidence: src/Filament/Resources/PrivacyRequests/PrivacyRequestResource.php, tests/Unit/PrivacyCenterAdminSurfaceTest.php | Done | M | High | §2 |
Shipped 2026-06-05: Reconcile manifest capabilities with reality. Evidence: removed privacy-center-cookie-categories; retained scheduled retention command/frequency metadata in capell.json; covered by tests/Unit/ManifestRequirementsTest.php | Done | S | High | §4 |
Shipped 2026-06-05: Expanded README/CHANGELOG/docs overview around shipped admin/console surfaces, DSAR export and erasure boundaries, consent mirroring, retention execution, install/config notes, and public-output safety limits. Evidence: README.md, CHANGELOG.md, docs/overview.md, tests/Unit/ManifestRequirementsTest.php | Done | S | Med | §4 |
Shipped 2026-06-05: Fail-loud when PrivacyIdentifier would otherwise fall back to the literal hash salt. Evidence: src/Support/PrivacyIdentifier.php, tests/Unit/PrivacyIdentifierTest.php | Done | S | Med | §2, §4 |
| Add table filters (status/overdue/category/decision/jurisdiction) to record resources | Done | S | Med | §2, §3 |
| Shipped 2026-06-06: Add “run now / dry-run” retention action surfacing matched/affected counts | Done | S | Med | §2 |
Shipped 2026-06-06: Cache the overview widget stats via the declared privacy-center tag + model-event invalidation | Done | M | Med | §2, §4 |
Done/Shipped: Add Filament interaction tests (render + fulfil stamps fulfilled_at) and a scheduled-job-runs test. Evidence now covers privacy-request workflow action mounting/stamping, retention dry-run/run-now table actions, and scheduled retention command/manifest alignment. | Done | M | Med | §4 |
| Done/Shipped: Build public cookie consent banner / preference centre (cache-safe, theme-aware) writing to the ledger + cache-safety test | Done | L | High | §3 — /privacy/consent records granular category decisions via RecordConsentAction, the banner partial links to the preference center, and HTTP coverage proves anonymous public output avoids admin/internal leakage. |
Cross-package ContributesSubjectData / ErasesSubjectData contract + registry (the suite differentiator) | Later | L | High | §3, §5 |
Public DSAR intake form with email verification populating verified_at | Later | L | High | §3 |
| Consent expiry/refresh job (granted→expired) + policy publish/retire workflow + signed consent-proof export | Later | M | Med | §3 |
| Geo/jurisdiction-aware policy & category selection (GDPR vs CCPA) | Later | L | Med | §3, §5 |