Skip to content

Customer Responsibility Tracking

Track your own obligations when a vendor holds a certification with shared-responsibility controls (SOC 2, ISO 27001, NIST CSF, or any framework you import).


Overview (for review)

The problem

When a vendor is SOC 2 or ISO 27001 certified, that certification doesn't mean you are compliant. These frameworks define a shared responsibility model — some controls are the vendor's job (they secure the platform), and some are yours (you enforce MFA for your users, you conduct access reviews, you classify the data you store).

Right now mitch-risk says "Vendor A is 94% compliant with SOC 2" — but if 8 of those controls are actually your responsibility and you haven't done them, you're not at 94%. You don't know where you stand.

What this feature does

When you record a vendor's certification, mitch-risk can automatically create a customer responsibility checklist — a list of controls that are designated as your responsibility under that certification's framework. This works for any framework, not just SOC 2.

┌─────────────────────────────────────────────────────────────┐
│ Customer Responsibility (SOC 2 Type II)                      │
│                                                              │
│ These controls are your responsibility under this            │
│ vendor's SOC 2 report.                         [Collapse ▲]  │
│                                                              │
│ Progress: ████████░░░░░░  4 of 12 completed                  │
│                                                              │
│ CC6.1   Enforce MFA for all users                  [✔ Done] │
│ CC6.2   Conduct quarterly access reviews    [▶ In Progress]  │
│ CC7.1   Classify stored data                  [○ Pending]   │
│ CC9.1   Review audit logs monthly             [○ Pending]   │
│ CC9.2   Conduct risk assessments annually     [○ Pending]   │
│                                                              │
│ Your compliance: 33%    ·  Vendor compliance: 94%            │
│ Combined risk posture: 76%                                   │
└─────────────────────────────────────────────────────────────┘

For each item in the checklist you can:

  • Track status — Pending, In Progress, Completed, or Not Applicable
  • Assign it to a team member
  • Add notes and attach evidence (screenshots, policy documents, audit records)
  • Mark it done — mitch-risk records when and by whom

The checklist feeds into:

  • The vendor compliance view — now split into "Vendor compliance" and "Your compliance"
  • The dashboard — "Your overall responsibility posture" across all vendors
  • The risk register — unfinished responsibility items surface alongside findings
  • The certification expiry reminders — already covered, reminders still go out

How it works (user perspective)

  1. Admin marks controls as shared responsibility — via the Frameworks settings page, an admin can check a box on any control to designate it as a customer responsibility. SOC 2 ships with 13 controls pre-marked based on the published shared-responsibility matrix. You can mark controls in any framework you've imported (ISO 27001, NIST CSF, Essential Eight, or your own custom CSV import).

  2. You add a certification to a vendor — existing flow: cert name, issuer, dates, attachment. Check the "Compliance actions required" checkbox and select the framework from a dropdown showing only frameworks with shared-responsibility controls. Actions are auto-generated for the selected framework.

  3. The checklist appears on the vendor detail page below the certification. No new pages, no extra navigation.

  4. Over time, you update each item as you implement the controls. Evidence can be attached per item.

  5. mitch-risk shows your progress alongside the vendor's own compliance score, rolling up into a combined risk posture.

How it's framework-agnostic

The mechanism is not hardcoded to SOC 2 or ISO 27001. It works against a single boolean on the Control model: isSharedResponsibility. Any control in any framework can be flagged — whether it ships pre-seeded (SOC 2), you add it manually (ISO 27001), or you import a custom framework via CSV.

The certification form includes a "Compliance actions required" checkbox. When checked, a dropdown appears showing only frameworks that have at least one control marked isSharedResponsibility = true. The user selects the framework explicitly — no name guessing or substring matching. When the cert is saved with a selected framework, actions are auto-generated.

Certification saved with frameworkName
  → If no frameworkName: no-ops (user didn't enable compliance actions)
  → Fetch controls in that Framework where isSharedResponsibility = true
  → Upsert CustomerResponsibilityAction rows for those controls

If a certification doesn't match a known framework, or if the matched framework has no shared-responsibility controls marked, nothing is generated. The admin can mark controls at any time via the Frameworks settings.

What ships pre-loaded

SOC 2 shared-responsibility controls (12 controls across CC6, CC7, CC8, CC9, PI1) are pre-seeded as isSharedResponsibility = true based on the published AICPA Trust Services Criteria shared-responsibility matrix.

What this doesn't do (out of scope)

  • It doesn't replace the existing questionnaire/assessment/scoring flow — that's still how you assess vendor compliance
  • It doesn't automatically verify that you've done the control (you self-attest)
  • It doesn't integrate with your own internal GRC tool
  • It doesn't enforce policy
  • It doesn't send email reminders for overdue actions (future cron enhancement)
  • It doesn't have dedicated API endpoints (server actions only in v1)

Technical Design

Data model

New model: CustomerResponsibilityAction

prisma
model CustomerResponsibilityAction {
  id                String                         @id @default(cuid())
  vendorId          String
  vendor            Vendor                         @relation(fields: [vendorId], references: [id], onDelete: Cascade)
  certificationId   String?
  certification     VendorCertification?           @relation(fields: [certificationId], references: [id], onDelete: SetNull)
  controlCode       String
  frameworkName     String
  controlTitle      String
  status            CustomerResponsibilityStatus   @default(PENDING)
  assignedToId      String?
  assignedTo        User?                          @relation(fields: [assignedToId], references: [id], onDelete: SetNull)
  notes             String?
  completedAt       DateTime?
  createdAt         DateTime                       @default(now())
  updatedAt         DateTime                       @updatedAt

  @@unique([vendorId, certificationId, controlCode])
  @@index([vendorId])
  @@index([vendorId, status])
  @@index([assignedToId])
  @@map("customer_responsibility_actions")
}

enum CustomerResponsibilityStatus {
  PENDING
  IN_PROGRESS
  COMPLETED
  NOT_APPLICABLE
}

Addition to Control model

prisma
model Control {
  // ... existing fields ...
  isSharedResponsibility Boolean @default(false)
}

How auto-generation works

When a certification is saved via saveCertificationAction() or handleCertificationAttachment():

  1. Explicit framework selection: The certification form includes a "Compliance actions required" checkbox. When checked, a dropdown appears showing only frameworks that have at least one control with isSharedResponsibility = true. The user selects the framework explicitly — no guessing or substring matching.

  2. Control lookup: All controls in the selected framework where isSharedResponsibility = true are fetched.

  3. Upsert: For each shared control, a CustomerResponsibilityAction is upserted on (vendorId, certificationId, controlCode). If a row already exists (from a previous save or a prior certification), the existing status, notes, and assignment are preserved.

  4. No framework, no action: If the "Compliance actions required" checkbox is unchecked, or no framework is selected, nothing is generated. The certification is saved as normal.

  5. Certification deletion: Linked responsibility actions have certificationId set to null (SetNull), preserving the implementation record. The checklist remains visible but shows "Certification removed" instead of the cert name.

Controls management UI

The Frameworks → Controls detail page (app/(internal)/frameworks/[frameworkId]/controls/[controlId]/page.tsx or the framework detail page's controls list) gains a "Shared Responsibility" checkbox per control, editable by users with FRAMEWORKS_EDIT. This is the same permission that allows editing framework metadata.

The checkbox appears as a toggle in an existing or new column on the controls list view, and as a field on the control detail/edit form. When toggled, the control's isSharedResponsibility flag is updated immediately.


Phased Implementation Plan

Phase 1 — Schema & seed

What it delivers: Migration applied, seed data correct, database layer working. Nothing visible to users yet.

| Task | Detail | |---|---|---| | Add CustomerResponsibilityAction model + CustomerResponsibilityStatus enum to prisma/schema.prisma | | | Add isSharedResponsibility field to Control model | | | Create + apply migration | | | Add isSharedResponsibility flags to SOC 2 seed data (prisma/seed-data/soc2.ts) | 13 controls pre-marked | | Update prisma/seed.ts to write isSharedResponsibility on framework upsert | | | Create lib/db/customer-responsibility.ts | listActionsByVendor(vendorId), listSharedControlsForFramework(frameworkName), listCertificationsForVendor(vendorId) | | Update lib/schemas/framework.ts — add optional isSharedResponsibility to CSV row schema | Accepts true/false/1/0/yes | | Update lib/actions/frameworks.ts — parse is_shared_responsibility column from CSV imports | Optional column, defaults to false when absent | | Update app/(internal)/frameworks/import/import-form.tsx — add column to template and help text | Downloadable CSV template includes the new column |

Gate:

  • [ ] Migration applies cleanly on fresh database
  • [ ] Seed runs idempotently — re-running doesn't duplicate controls or flags
  • [ ] listActionsByVendor() returns empty array on a vendor with no certs
  • [ ] listFrameworksWithSharedControls() returns only frameworks with at least one shared control
  • [ ] CSV import with is_shared_responsibility column correctly marks controls
  • [ ] CSV import without is_shared_responsibility column defaults controls to false
  • [ ] npm run typecheck, npm run lint, npm run build clean

Effort: ~0.5 day


Phase 2 — Auto-generation on certification save

What it delivers: When a certification matching a known framework is saved on a vendor, shared-responsibility actions are automatically created. Verifiable by saving a cert and checking the DB.

TaskDetail
Complete upsertActionsForCertification() in lib/db/customer-responsibility.tsMatch cert → framework → find shared controls → upsert rows
Wire into saveCertificationAction() in lib/actions/certifications.tsAfter cert save, call auto-generation
Handle edge cases: cert doesn't match any framework (no-ops), framework has zero shared controls (no-ops), re-saving same cert (idempotent upsert), deleting a cert (SetNull on linked actions)
Add zod schema customerResponsibilityActionSchema to lib/schemas/certification.tsFor future write operations

Gate:

  • [ ] Saving a SOC 2 certification on a vendor creates ~12 PENDING actions in the DB
  • [ ] Saving a certification that doesn't match any known framework creates zero actions
  • [ ] Saving a certification on NIST CSF with one shared control creates exactly one action
  • [ ] Re-saving the same certification does NOT duplicate actions
  • [ ] Deleting a certification sets action.certificationId = null (preserves action data)
  • [ ] npm run typecheck, npm run lint, npm run build clean

Effort: ~0.5 day


Phase 3 — Read-only checklist on vendor detail

What it delivers: The customer responsibility checklist appears on the vendor detail page as a read-only card. Users can see what's needed but can't interact yet.

TaskDetail
Create components/customer-responsibility-manager.tsxServer component: fetches actions by vendorId, groups by certification
Create components/customer-responsibility-checklist.tsxClient component: renders a collapsible card per certification, lists actions with status badges, shows progress bar
Wire into app/(internal)/vendors/[vendorId]/page.tsxNew section below Certifications card
Handle empty states: vendor with no certs (nothing shown), vendor with cert but no actions (informational message), vendor with cert where all shared controls completed (full progress bar)
Progress bar: completed / total with color coding (red < 50%, amber < 100%, green 100%)

Gate:

  • [ ] Vendor with SOC 2 cert shows "Customer Responsibility" card(s) grouped by certification
  • [ ] Card shows: certification name, progress bar, list of controls with status badges
  • [ ] Vendor with no certifications has no customer responsibility section
  • [ ] Vendor with a certification that has no shared controls shows no checklist, or an informational message
  • [ ] UI elements gated by VENDORS_VIEW permission (server-side guard)
  • [ ] npm run typecheck, npm run lint, npm run build clean

Effort: ~1 day


Phase 4 — Checklist interactivity

What it delivers: Full CRUD on checklist items — status changes, notes, user assignment, evidence upload. The checklist becomes actionable.

TaskDetail
Add updateAction(actionId, data) to lib/db/customer-responsibility.tsUpdate status, notes, assignedToId, completedAt
Add bulkUpdateActions() to lib/db/customer-responsibility.tsMark-all-as type operations
Create lib/actions/customer-responsibility.tsServer action: updateResponsibilityAction(), gated by VENDORS_EDIT
Expand customer-responsibility-checklist.tsxInline status dropdown (Pending / In Progress / Completed / Not Applicable), notes textarea, assigned-to user picker
Add evidence upload per actionReuse existing Attachment model (entityType = "CustomerResponsibilityAction"), existing file upload pattern from certifications
Auto-calculate completedAtMarking Complete auto-sets timestamp; unmarking clears it
Audit loggingUPDATE_RESPONSIBILITY_ACTION, SET_RESPONSIBILITY_ACTION audit entries
RBAC on all write controlsHide status dropdown, notes field, assignment picker, and upload button when user lacks VENDORS_EDIT

Gate:

  • [ ] Each action can be set to Pending / In Progress / Completed / Not Applicable via dropdown
  • [ ] Marking Complete auto-sets completedAt; changing away from Complete clears it
  • [ ] Actions can be assigned to an internal user via a user picker
  • [ ] Notes can be added and edited inline per action
  • [ ] Evidence files can be attached and removed per action (reuses existing Attachment infrastructure)
  • [ ] All write controls hidden when user lacks VENDORS_EDIT
  • [ ] Progress bar updates in real time when an action status changes
  • [ ] npm run typecheck, npm run lint, npm run build clean

Effort: ~1.5 days


Phase 5 — Controls management UI ("Shared Responsibility" checkbox)

What it delivers: Admins can mark controls as shared responsibility via the Frameworks settings. This makes the feature work for any framework, not just SOC 2.

TaskDetail
Add updateControlSharedResponsibility(controlId, value) to lib/db/frameworks.ts or a new helperSimple boolean update on Control
Add server action toggleSharedResponsibilityAction() gated by FRAMEWORKS_EDIT
Add a "Shared Responsibility" checkbox column to the controls list on the framework detail pageapp/(internal)/frameworks/[frameworkId]/page.tsx
Add the checkbox to the control detail/edit pageapp/(internal)/frameworks/[frameworkId]/controls/[controlId]/page.tsx
Audit loggingMARK_CONTROL_SHARED, UNMARK_CONTROL_SHARED audit entries

Gate:

  • [ ] Framework detail page shows a "Shared Responsibility" column on the controls table
  • [ ] Toggling a control's checkbox persists immediately
  • [ ] Control detail page has a "Shared Responsibility" toggle
  • [ ] Controls visible/hidden based on FRAMEWORKS_EDIT permission
  • [ ] Marking a control as shared does NOT retroactively generate actions for existing certifications (Phase 2 handles generation on cert save only, which is intentional — the admin re-saves the cert if needed)
  • [ ] npm run typecheck, npm run lint, npm run build clean

Effort: ~0.5 day


Phase 6 — Scoring & risk register integration

What it delivers: Customer responsibility feeds into the compliance view and risk register. Combined risk posture is visible.

TaskDetail
Add getCustomerResponsibilityCompliance(vendorId) to lib/db/customer-responsibility.tsReturns { total, completed, inProgress, pending, n_a, percent } per vendor
Add getPortfolioResponsibilitySummary() to lib/db/customer-responsibility.tsAggregated across all vendors
Update lib/db/compliance.ts or create new helperReturns split metrics: vendor compliance + customer compliance
Update vendor detail compliance tabShow "Vendor compliance" and "Your compliance" side-by-side metrics with combined risk calculation
Update app/(internal)/risk-register/page.tsxSurface PENDING + IN_PROGRESS responsibility actions alongside findings
Add filter toggle to risk register"Show customer responsibility items" checkbox or tab
Dashboard widget"Your overall responsibility posture" stat card (optional, can defer to a follow-up phase)

Gate:

  • [ ] Vendor compliance tab shows both metrics when shared controls exist for that vendor
  • [ ] Combined risk posture updates when actions are completed
  • [ ] Risk register shows responsibility actions with vendor, control code, status, assignee
  • [ ] Risk register filter toggles between findings-only and findings + responsibility actions
  • [ ] No regression on vendors without shared-responsibility controls
  • [ ] npm run typecheck, npm run lint, npm run build clean

Effort: ~1 day


Phase 7 — Tests & polish

What it delivers: Full test coverage, empty states, loading states, edge-case handling.

TaskDetail
Unit tests for lib/db/customer-responsibility.tsupsertActionsForCertification, updateAction, getCustomerResponsibilityCompliance, framework matching logic
Integration tests for server actionsAuto-generation on cert save, status transitions, evidence upload, cert deletion
Unit tests for edge casesDuplicate cert save, cert with no matching framework, framework with zero shared controls, vendor with no certs
Playwright e2eFull flow: admin marks a control as shared → add cert → checklist appears → complete an action → verify score updates
Empty statesNo certs, cert with no shared controls, all actions completed
Loading statesSkeleton for checklist loading
PolishConsistent badge colors, progress bar animation, responsive layout

Gate:

  • [ ] All unit tests pass
  • [ ] All integration tests pass
  • [ ] Playwright e2e passes against production build
  • [ ] Empty states render cleanly
  • [ ] No unhandled edge cases
  • [ ] npm run lint, npm run typecheck, npm run build clean
  • [ ] npm run test passes

Effort: ~1 day


Summary

PhaseDeliverableEffort
1Schema + seed0.5 day
2Auto-generation on cert save0.5 day
3Read-only checklist UI1 day
4Checklist interactivity1.5 days
5Controls management UI (shared checkbox)0.5 day
6Scoring + risk register integration1 day
7Tests + polish1 day
Total~6 days

Each phase is independently shippable. You can stop after Phase 3 and have a useful read-only checklist, ship Phase 4 to let teams start tracking items, and add Phase 5 when you're ready to support frameworks beyond SOC 2.


File manifest

FileActionPhase
prisma/schema.prismaEdit1
prisma/migrations/New1
prisma/seed.tsEdit1
prisma/seed-data/soc2.tsEdit1
lib/db/customer-responsibility.tsNew1-2, 4, 6
lib/actions/customer-responsibility.tsNew4, 5
lib/actions/certifications.tsEdit2
lib/schemas/certification.tsEdit2
components/customer-responsibility-manager.tsxNew3
components/customer-responsibility-checklist.tsxNew3-4
app/(internal)/vendors/[vendorId]/page.tsxEdit3, 6
app/(internal)/frameworks/[frameworkId]/page.tsxEdit5
app/(internal)/frameworks/[frameworkId]/controls/[controlId]/page.tsxEdit5
lib/db/frameworks.tsEdit5
lib/db/compliance.tsEdit6
app/(internal)/risk-register/page.tsxEdit6
lib/permissions.tsEdit4-5

RBAC

ActionPermission
View customer responsibility checklistVENDORS_VIEW (existing)
Update status, assign, add notes, upload evidenceVENDORS_EDIT (existing)
Mark controls as shared responsibilityFRAMEWORKS_EDIT (existing)

No new permissions needed.

Seed data

SOC 2 shared-responsibility controls (based on AICPA Trust Services Criteria published shared-responsibility matrix):

CodeControl
CC6.1Logical and physical access controls
CC6.2User access provisioning and review
CC6.3Security incident response
CC6.4Security monitoring and detection
CC6.6External boundary protection (your network)
CC6.7Data transmission encryption
CC7.1Data classification and labeling
CC7.2Data retention and disposal
CC8.1Change management for your systems
CC9.1Risk assessment process
CC9.2Vendor risk management (for your other vendors)
PI1.3Privacy notice and consent collection
PI1.4Individual data access and correction rights

Scoring impact

The vendor compliance tab splits into two metrics when shared-responsibility controls exist:

┌──────────────────┐  ┌──────────────────┐
│ Vendor compliance│  │ Your compliance  │
│                  │  │                  │
│     94%          │  │      67%         │
│  (answers +      │  │  (your completed ÷
│   review)        │  │   total shared)  │
└──────────────────┘  └──────────────────┘

Combined risk posture: 81%

Combined risk is a weighted average (configurable in Scoring settings, default 50/50 split). When a vendor has no shared-responsibility controls, only the vendor compliance score is shown (no change from current behavior).

Customer responsibility on risk register

Pending and In Progress actions surface in the risk register alongside findings, with a dedicated filter toggle. Each entry shows:

  • Vendor name → link to vendor detail
  • Control code + title
  • Status badge (colored)
  • Assigned to
  • Days since certification was recorded (aging)

Dependencies

None. Everything reuses existing infrastructure:

  • Attachment model for evidence (entityType = "CustomerResponsibilityAction")
  • /api/attachments/[attachmentId] for serving
  • VendorCertification for linking
  • Control for control definitions
  • Framework for framework matching
  • User for assignment
  • AuditLog for audit trail

Out of scope

  • Custom per-certification shared responsibility matrices (v1 is framework-driven)
  • Email reminders for overdue actions (future cron enhancement)
  • API endpoints for responsibility actions (server actions only in v1)
  • Integration with external GRC systems
  • Retroactive generation of actions when a control is newly marked as shared (requires re-saving the certification)
  • Org-wide "customer responsibility policy" settings

Open-source, self-hosted third party vendor risk management.