"""Add musehub_auth_keys table for Ed25519 public-key authentication. Each row is one registered Ed25519 public key that may authenticate as a MuseHub identity. The challenge-response flow is stateless (nonces are short-lived hex tokens); only the long-lived public key registration lives here. Revision ID: 0007 Revises: 0006 """ from __future__ import annotations import sqlalchemy as sa from alembic import op revision = "0007" down_revision = "0006" branch_labels = None depends_on = None def upgrade() -> None: op.create_table( "musehub_auth_keys", sa.Column("key_id", sa.String(36), primary_key=True), sa.Column( "identity_id", sa.String(36), sa.ForeignKey("musehub_identities.id", ondelete="CASCADE"), nullable=False, ), sa.Column("public_key_b64", sa.String(64), nullable=False), sa.Column("fingerprint", sa.String(64), nullable=False), sa.Column("label", sa.String(255), nullable=False, server_default=""), sa.Column( "created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now(), ), sa.Column("last_used_at", sa.DateTime(timezone=True), nullable=True), sa.UniqueConstraint("fingerprint", name="uq_musehub_auth_keys_fingerprint"), ) op.create_index("ix_musehub_auth_keys_identity_id", "musehub_auth_keys", ["identity_id"]) def downgrade() -> None: op.drop_index("ix_musehub_auth_keys_identity_id", table_name="musehub_auth_keys") op.drop_table("musehub_auth_keys")