Internal Ticketing Runtime
Status
Implemented
Related Requirements
- Client response references: Phase 1 canonical OneProtect internal ticketing; Jira/ServiceNow/Freshdesk/Zendesk/HaloPSA remain Phase 2 integrations.
- ADR references:
docs/adr/ADR-0010-internal-ticketing-contracts.md. - Task board references: OP-033 and OP-033R.
Problem Statement
OneProtect needs a canonical tenant-scoped investigation record. Generic webhook delivery exists, but outbound delivery is not the same as an internal ticket store.
Architectural Intent
Tickets link alerts, assets, evidence, delivery attempts, audit records, and operator comments into one tenant-owned work item. External ticketing products can mirror or receive data later through adapters, but they do not own OneProtect's lifecycle state.
What Was Implemented
- ADR-0010 accepted the internal ticket/work-item model.
- Postgres/RLS and SQLite local tables for tickets, ticket events, comments, and links.
- FastAPI ticket routes for manual creation, alert-to-ticket creation, list, detail, status/assignment updates, comments, and links.
- Idempotent alert-to-ticket creation.
- Event publication for
ticket.created,ticket.status_changed,ticket.assigned,ticket.comment_added, andticket.linked. - Worker handling for
ticket.created. - Basic API-backed Tickets console page.
- Contract validation continues to cover ticket event payloads.
Components Involved
- OpenAPI:
specs/openapi.yaml - AsyncAPI:
specs/asyncapi.yaml - Event schemas:
specs/events/ticket.*.v1.schema.json - Architecture docs:
docs/architecture/internal-ticketing-contracts.md - ADRs:
docs/adr/ADR-0010-internal-ticketing-contracts.md - Runtime migration:
db/postgres/007_internal_ticketing_runtime.sql - Runtime service:
poc/ingest_api/ticket_service.py - UI:
frontend/src/app/console/tickets/page.tsx
APIs / Events / Schemas
- API:
POST /api/v1/alerts/{alert_id}/ticket - API:
GET /api/v1/tickets - API:
POST /api/v1/tickets - API:
PATCH /api/v1/tickets/{ticket_id} - Event:
ticket.created - Event:
ticket.status_changed - Event:
ticket.assigned - Event:
ticket.comment_added - Event:
ticket.linked
Deployment Notes
No new pods or infrastructure. Existing migration paths apply
db/postgres/007_internal_ticketing_runtime.sql through local Compose and the
application migration runner. External ticketing adapters remain future work.
Security / Tenant Isolation
All tickets, events, comments, links, and audit records are tenant-scoped.
Comments use internal or auditor_visible visibility. Auditor reads are
read-only and redacted.
Validation Steps
UI Validation
cd frontend && npm test -- --run
API Validation
make validate-contracts
ONEPROTECT_DB_BACKEND=sqlite ONEPROTECT_WEBHOOK_ADAPTER_MODE=mock .venv/bin/python -m unittest tests.test_internal_ticketing
Smoke Validation
make test-sqlite
Known Limitations
- External Jira/ServiceNow/Freshdesk/Zendesk/HaloPSA adapters are not implemented.
- The Tickets page now has OP-066a mutation controls for create, status/priority/assignee updates, comments, and links. Alert triage, bulk actions, and external adapter mirroring UI remain separately scoped.
- Exports follow ADR-0014 and are not part of this runtime slice.
Follow-Up Work
- Keep external ticketing adapters behind integration seams.
- Add scoped alert triage, bulk action, and external adapter UI only after backend APIs are stable and tested.
- Mirror OneProtect tickets to external systems only through future adapters.
Acceptance Criteria Mapping
| Acceptance criterion | Evidence |
|---|---|
| Canonical ticket/work-item model is accepted | docs/architecture/internal-ticketing-contracts.md, ADR-0010 |
| Alert-to-ticket flow is idempotent and implemented | poc/ingest_api/ticket_service.py, tests/test_internal_ticketing.py |
| Status lifecycle and role rules are enforced | poc/ingest_api/ticket_service.py, tests/test_internal_ticketing.py |
| Audit and event contracts are emitted | specs/asyncapi.yaml, scripts/validate_contracts.py, tests/test_internal_ticketing.py |
| Basic UI reads and OP-066a mutations use APIs | frontend/src/app/console/tickets/page.tsx, frontend/src/app/console-api/oneprotect/tickets/, frontend/src/test/oneprotect-ui.test.tsx |