Skip to content

Accounts: /account consent and CyberdyneAuth provisioning requests - #105

Merged
leonardoaraujosantos merged 4 commits into
mainfrom
feat/account-consent
Sep 27, 2026
Merged

leonardoaraujosantos merged 4 commits into
mainfrom
feat/account-consent

Conversation

@leonardoaraujosantos

Copy link
Copy Markdown
Contributor

Adds /account: a person asks for a CyberdyneAuth account in a DM, sees exactly the name and email that will be sent, and confirms. Records consent (email only as an HMAC under PROVISIONING_EMAIL_KEY), counted provisioning requests (1/24h, 3/30d), single-use 15-minute link codes, and the person_account_link table. Ingest ages out old requests and codes.

Migration 0035 (revises 0033): the four account tables, each added to purge_person_derived so opt-out and erasure remove them.

Off by default (ACCOUNT_PROVISIONING_ENABLED); needs PROVISIONING_EMAIL_KEY (>= 32 chars), ADMIN_PUBLIC_URL and a provisioner (later PR) to be offered.

Tests: unit + integration + e2e green locally; alembic upgrade / downgrade -1 / upgrade clean; mypy, ruff, openspec strict clean.

…sioning port

/account create shows the exact name, email and language in a DM, states
that the invitation must be accepted and that the CyberdyneAuth account
outlives Delete everything, and sends only those three values through the
AccountProvisioner port on Confirm. Requests are limited to 1/24h and 3/30d
per person under a lock on the person row; a provider refusal is uncounted.
Emails are stored only as HMAC-SHA256(PROVISIONING_EMAIL_KEY, email).
/account link and [Link my account] DM a single-use 15-minute link code,
stored as sha256, superseding earlier codes, 5 per day.

Migration 0035 adds account_consent, account_provisioning_request,
account_link_code and person_account_link, all purged by
purge_person_derived; ingest deletes requests past 30 days and codes past a
day. Off unless ACCOUNT_PROVISIONING_ENABLED, and hidden until a provisioner
is wired: only the e2e harness has one (always 202).
…lic URL

The name sent on /account confirm is now the first of full name, preferred
name and display name that passes the full-name fact rule and is not
domain-like, so no link, <, >, control or bidi character is sent or shown in
the consent; if none passes, no name is sent. Previously the display name
went through unchecked.

ADMIN_PUBLIC_URL is validated as the console does (https, a host, no query
or fragment), since the link code is appended to it as a bearer secret.

The parallel-reservation test now pauses every transaction between counting
and inserting, so it fails if the person-row lock is removed.
# Conflicts:
#	src/chatmemory/composition.py
#	tests/unit/test_sql_audit.py
@leonardoaraujosantos
leonardoaraujosantos merged commit 61639db into main Sep 27, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant