Publish and rollback
Policies are versioned. You publish drafts to activate them, and you roll back to previous versions when something goes wrong.
The model
- Every edit creates a new row in the
policiestable withpublished_at = NULL— a draft. - Publish stamps
published_at = clock_timestamp()on a draft. The latestpublished_atis the active version. - Rollback copies an older version’s body into a brand-new draft and publishes it. The old version stays in the table. No destructive operations.
This means every change is reversible by going forward, not backward — you create a new published row.
Via the dashboard
Open /policies/{class_id}. The versions rail on the left shows every version with:
- a green dot = currently active
- a grey dot = published, not active
- a teal dot = draft (unpublished)
Click any version to see its body on the right. Then:
| Source state | Available action |
|---|---|
| Draft | Publish this draft — stamps published_at, becomes active |
| Published but not active | Rollback to this — creates a new draft from this body and publishes it |
| Currently active | (no actions — already in use) |
Via the CLI
There are no CLI commands for policy lifecycle yet. Use HTTP or the dashboard.
Via HTTP
List versions for a class
curl http://localhost:8000/api/v1/policy/class/<class_id>/versionsReturns newest first. The active one has published_at set; the rest are drafts or older published versions.
Get the active policy
curl http://localhost:8000/api/v1/policy/class/<class_id>/activeCreate a draft
curl -X POST http://localhost:8000/api/v1/policy/class/<class_id>/drafts \ -H "Content-Type: application/json" \ -d @new-policy-body.jsonReturns 201 Created with the new row, published_at: null.
Publish a draft
curl -X POST http://localhost:8000/api/v1/policy/<policy_id>/publishReturns the same row with published_at set.
Rollback to a previous version
curl -X POST http://localhost:8000/api/v1/policy/class/<class_id>/rollback/<target_version>Returns a brand-new published row whose body is copied from target_version.
What you can’t do
- Edit a published row in place — no
PUTorPATCHon policy bodies. Always create a new draft. - Delete a version — nothing destructive. If a body is sensitive, rotate to a newer draft and the older content stays in the table for audit; full deletion is a Postgres-level operation.
- Publish two drafts to the same version — each draft creates a new monotonic version number per class.
Quayside re-validates on rollback
When you roll back to version N, Quayside re-validates the old body through the current Pydantic schema before publishing the new copy. This means: if the schema has tightened since you originally wrote version N, the rollback fails — you have to write a fresh draft instead. That’s a feature, not a bug.
Audit
Every policy version stays in the table forever. Joining your access logs against the published_at timestamps gives you a complete history of “what policy was active at time T, and who pushed the change.” For deployments with stricter compliance needs, layer a separate “who clicked publish” log on top of the API endpoints.