audit-1 (critical 1): mandatory operator signatures bound to content+deployment
- signing module: canonical content hashes + approval_message sha256(cayce-ops:<type>:<deployment_id>:<id>:<content_hash>) - approve/approve-draft/publisher-approve REQUIRE signature; without CAYCE_OPS_OPERATOR_PUBKEY+DEPLOYMENT_ID the daemons refuse to execute/send/publish (no degraded mode) - run-time re-verification at execute/send/publish: unsigned rows (raw SQL approval) or edited content revert to pre-approval state, audited as error - FULL signatures stored on rows (operator_signature, migration 0003) and in audit detail — never truncated - signature failures audited from CLI wrappers too (1f) - AUTONOMOUS_MODE approval path removed (D13): no non-human approvals tests: SQL-approved row refused at execute/send/publish; to_addr change blocks send; foreign deployment/content signatures refused; full-signature persistence. 65 tests green.
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
"""approval hardening: mandatory operator signatures bound to content
|
||||
|
||||
Revision ID: 0003
|
||||
Revises: 0002
|
||||
Create Date: 2026-10-20
|
||||
|
||||
- proposals.operator_signature
|
||||
- mail_messages.operator_signature
|
||||
- content_items.operator_signature
|
||||
|
||||
Full signatures (128 hex chars) — never truncated. Existing NULL rows are
|
||||
refused at execution/send/publish time by the signing gate (no degraded mode).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision = "0003"
|
||||
down_revision = "0002"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
with op.batch_alter_table("proposals") as batch:
|
||||
batch.add_column(sa.Column("operator_signature", sa.Text(), nullable=True))
|
||||
with op.batch_alter_table("mail_messages") as batch:
|
||||
batch.add_column(sa.Column("operator_signature", sa.Text(), nullable=True))
|
||||
with op.batch_alter_table("content_items") as batch:
|
||||
batch.add_column(sa.Column("operator_signature", sa.Text(), nullable=True))
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
with op.batch_alter_table("content_items") as batch:
|
||||
batch.drop_column("operator_signature")
|
||||
with op.batch_alter_table("mail_messages") as batch:
|
||||
batch.drop_column("operator_signature")
|
||||
with op.batch_alter_table("proposals") as batch:
|
||||
batch.drop_column("operator_signature")
|
||||
Reference in New Issue
Block a user