Skip to content

Secure cross-platform account linking #16

Open

Description

@NinjaLikesCheez

Context

Tiers are tied to Discord IDs. For Pixel to recognise a member on Telegram (#17) or any future interactive platform, they have to prove that the new account belongs to the same person as a trusted Discord account. This is the most security-sensitive feature in the roadmap.

The identity doc sketches the flow:

  1. The person asks for a one-time link code from their (trusted) Discord account. The code is shown only to them.
  2. They redeem it in a private chat on the new platform.
  3. Codes:
    • are high-entropy, short-lived and single-use
    • are stored only as a hash
    • are rate-limited
    • are redeemed safely even if two attempts race
  4. Both accounts are notified when a link happens, with an easy way to undo it.

Depends on #15 (database).

Goal

People can securely link and unlink accounts across platforms. Linked accounts share one identity and tier.

Scope

  • An identity model: a person with one or more platform accounts
  • Commands: request a link code, redeem it, unlink, and show linked accounts in /whoami
  • Tiers: identity resolution works through linked accounts, so a member's Telegram account resolves to their tier
  • Audit: an audit record for every link and unlink, plus notifications to both accounts
  • Abuse limits: rate limits per user, per code, and overall

Acceptance criteria

  • Threat cases have tests:
    • expired, reused and wrong codes
    • redemption attempted in a group chat
    • brute-force attempts
    • two concurrent redemptions of the same code
    • unlinking
  • The plaintext code is never stored or logged
  • A link never grants more than the trusted account's tier
  • docs/identity-and-access.md is updated, plus an ADR
  • Security review by a second person before merge

Considerations

  • The direction of trust: codes always go from an already-trusted account to the new one, never the reverse
  • Should every person get a database identity when they first interact, or only when they link? Minimise stored data
  • /forget-me: deleting a person and their links (GDPR, Privacy notice and data retention (GDPR) #7)
  • Tiers come from Pixel's own data (the access store, Writable access store: members, friends and capabilities managed by admin commands #25), not from Discord roles, so a linked account simply shares that record. There's no role-freshness problem for people who only use Telegram. (This replaces the earlier idea of reading Discord roles, Discord role sync tier source #14, which was closed.)
  • The access store is keyed by platform ID today (Discord). Linking is what lets other platforms' accounts point at the same person

References

  • docs/identity-and-access.md: Later phases

Activity

  1. added
    enhancementNew feature or request
    securityAuth, access control, privacy, secrets — needs human review
    on Oct 3, 2026
  2. NinjaLikesCheez commented on Oct 7, 2026

    @NinjaLikesCheez
    ContributorAuthor

    Decision (2026-10-07): no database

    The access files already allow several IDs per person (ids: ["discord:…", "telegram:…"]), and the access store already writes members.yaml safely and audits every change. So linking writes there, and Postgres (#15) isn't needed.

    • /link on Discord: a private one-time code. It has about 40 bits of entropy, is kept only as a hash, in memory, is valid for 10 minutes and can be used once. Codes pending at a restart are lost, which is fine (Pixel is a single instance).
    • /link <code> in a Telegram DM: redeeming it adds telegram:<id> to the person's entry through the access store, which audits it. Both accounts are told. It's rate-limited per Telegram user.
    • Admins: entries in admins.yaml aren't written at runtime, so an admin's Telegram ID is added by hand in both files.
    • /unlink from either side. An admin can also remove an ID by editing the file.
    • Guests can't link: there's no entry to add to.
    • Threat model: a linked Telegram account carries the person's full tier and capabilities, including ha-doors. The runbook will cover unlinking, and /admin doors off, if an account is compromised.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestsecurityAuth, access control, privacy, secrets — needs human review

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions