Skip to content

0056 — Reputation composition + multi-account detection (Group G)

Status

Accepted.

Context

Seven audit findings cluster around two adjacent problems:

  • Reputation systems composition (N-D1, N-V1, X-F2, N-F2): faction reputation (per ADR-0018, ADR-0019, ADR-0032) and personal reputation (combat / Wanted / trade-volume) reference each other without a defined gating rule. The per-day rep-gain throttle is per-faction, so multi-faction-trigger actions multiply. The cascade rule has no break-out path, so a player drifting into Pirate alignment gets locked out of Federation indefinitely.
  • Multi-account exploits (E-V5, N-V2, N-V3): the same substrate (one human running multiple accounts) shows up in station-takeover volume metrics, message-beacon spam, and small-region governance voting. Each surface had its own ad-hoc defence; nothing was shared.

The two clusters share an underlying truth: the subscription tier is the legitimacy gate. A player paying for $5/mo Galactic Citizen or $25/mo Region Owner is a legitimate account regardless of household clustering with other paid accounts. Multi-account "exploits" are cheap only when the alts are free-tier.

Decision

N-D1 — Faction-rep and personal-rep are disjoint by default

The two reputation axes are independent signals. They do not cross-feed unless explicitly enumerated in the canonical action-delta table.

  • Faction reputation is event-driven: discrete actions in the faction-action table per ADR-0018 and ADR-0032 emit faction_rep_delta events.
  • Personal reputation is action-derived: PvP kills, Wanted-status changes, trade-volume contributions emit personal_rep_delta events.

Composition is closed: a single in-game action can carry both deltas (the table column has both signed integers); they are applied in parallel inside the same transaction. The table is the source of truth — no implicit "personal-rep change leaks into faction rep" or "faction standing dampens personal rep change" rules exist.

Cross-feeds that do exist must be enumerated explicitly in the action table as a third column. The Launch list:

Source Trigger Cross-feed
Personal → Faction Entering Wanted Status −50 rep with all Federation factions; +25 rep with all Pirate factions; one-shot at the Wanted-Status state-edge, not per-action
Personal → Faction Wanted Status clears (timer expiry or escape pod) No reverse cross-feed; the original deltas are not refunded

No other cross-feeds are canonical at Launch. Future cross-feeds must land in this table via a new ADR.

N-V1 — Global daily rep-gain throttle pool

Each player has a single daily_faction_rep_pool cap (Launch value: 100 rep / day) that applies to the sum of positive faction-rep deltas across all factions. Per-faction sub-caps remain (50/day per faction, per ADR-0018). When the global cap is hit, further faction-rep gains for that day are zeroed out — the action itself still applies (cargo delivered, NPC killed) but the rep delta drops to 0.

The cap resets at the player's local midnight (subscription-derived timezone, fall back to UTC). Negative deltas (rep losses) are not throttled — a player can lose more than 100 rep in a day if they earn it.

Player.daily_faction_rep_consumed Integer DEFAULT 0 and Player.daily_faction_rep_consumed_date Date track the pool. The throttle service (per ADR-0053) sweeps these at local-midnight rollover.

X-F2 — Composition closed by N-D1 + N-V1

The drip + delta + throttle interaction now has a single canonical answer: faction and personal rep are independent (N-D1); faction-rep gains share one global daily pool (N-V1); personal rep has its own existing gating; cross-feeds are in the table. Nothing further to decide.

N-F2 — Cascade-lockout redemption arc

Two new entries in the canonical action-delta table give cascade-locked players a discrete break-out path:

Action Faction-rep delta Notes
Voluntarily destroy a captured pirate Stronghold or Outpost +500 Federation, −500 Pirate Sacrifices the captured holding (no salvage, no compensation). Clears Pirate-alignment cascade lockout. One-shot per captured holding; can be repeated across multiple holdings. Bypasses the daily faction-rep pool cap (this is the recovery vehicle; the cap would defeat the purpose).
Donate captured pirate holding's wealth to Federation +200 Federation, −100 Pirate The captured holding's stored credits + commodity inventory transfer to Federation; the holding itself remains the player's. Subject to the daily pool cap.

The player must own (have captured) a pirate holding to use either action — these are not actions tourists or new accounts can stumble into. ARIA narrates these options when the player's faction-rep is in cascade-lock state, so the path is discoverable without a dedicated UI surface.

E-V5 — Multi-account detection layer, subscription-tier gated

A MultiAccountDetectionService lives in OPERATIONS as shared infra used by station-volume, governance, beacons, and faction farming. The detection model is subscription-tier aware:

  • Cluster signals (any combination flags a cluster):
  • Same payment method on file (hard signal).
  • Same active session token across different player IDs (hard signal — should never happen legitimately).
  • Same IP address within a 24-hour window (soft signal).
  • Same device fingerprint hash (soft signal).
  • Perfectly-correlated trade timings or governance voting patterns (soft signal — behavioural).

  • Block / discount logic depends on the subscription tier of the clustered accounts:

  • All clustered accounts have active paid subscriptions (Galactic Citizen or Region Owner): no block, no discount. Two people in a household paying $5/mo each are two legitimate accounts; one person paying for five GC subscriptions ($25/mo) is paying their way and gets five seats. Soft signals still surface to admin review for monitoring but do not penalize.
  • At least one free-tier account in the cluster: hard signals (same payment method, same session token) block team formation and discount the free account's metric contribution to . Soft signals discount the free account's contribution to 0.5× (votes, station volume, beacon weight).
  • All free-tier: same as above — hard signals block, soft signals 0.5×.

The principle: payment is the legitimacy gate. The multi-account exploit gets its leverage from being cheap (alts are free); paid alts pay their own way and the exploit isn't an exploit anymore — it's just a customer with five subscriptions.

N-V2 — Beacon placement gates

Three gates land on message-beacon placement (per ../FEATURES/gameplay/message-beacons.md):

  • Per-sector visibility cap: 10 beacons visible per sector at any time. Oldest beacon is auto-displaced when the 11th is placed.
  • Personal-rep gate: placement requires personal_rep ≥ neutral (not Wanted, not deeply negative). Negative-rep accounts cannot post beacons; existing beacons by accounts that subsequently go negative remain visible until they expire or are displaced.
  • Multi-account discount: free-tier accounts in a flagged cluster have beacon weight 0× (their beacons don't count toward the cap and aren't surfaced in the visibility list). Paid-tier accounts unaffected per E-V5.

N-V3 — Governance voting eligibility

Voting in regional governance requires all three:

  • Account age ≥ 60 days (Player.created_at + 60 days ≤ now()).
  • Personal rep ≥ neutral (not Wanted, not deeply negative; specific threshold defined in ../FEATURES/gameplay/ranking.md).
  • Multi-account clear or paid-tier: per E-V5 — paid accounts vote at full weight regardless of clustering; flagged free-tier accounts vote at 0.5× or 0× per signal severity.

The 60-day gate is set deliberately above the typical "spin up alts, play for two weeks" exploit horizon. Genuine new players in a fresh region wait two cycles to vote — acceptable trade-off given regional elections are the most concentrated exploit surface.

Consequences

  • The action-delta table (per ADR-0018, ADR-0032) gains a third column for personal-rep cross-feeds. Existing rows backfill with 0 for the new column.
  • Player schema gains daily_faction_rep_consumed Integer and daily_faction_rep_consumed_date Date.
  • The MultiAccountDetectionService is new infrastructure. It runs as a periodic job (every 1h is sufficient — alt-detection doesn't need real-time) and writes a MultiAccountCluster row per detected cluster, plus per-account MultiAccountFlag rows referencing the cluster. The flag carries the signal type and severity (hard_block / soft_signal).
  • Station-volume, governance, beacons, and faction-farming endpoints all read from MultiAccountFlag to apply the appropriate discount. Existing endpoints need a one-line check; the discount math is centralized.
  • Subscription-tier gating means the multi-account layer must read live subscription state. Cache TTL of 60s is sufficient — subscription tier changes are not high-frequency.
  • The cascade-redemption actions (N-F2) bypass the daily pool cap — that's the only canonical exception. Future "bypass cap" actions must be explicit ADR-level decisions.

Alternatives considered

  • Cross-feed faction and personal rep automatically. Rejected — too much hidden state. Players need to predict rep changes; ambient cross-feeds defeat that.
  • Per-faction throttle only (no global pool). Rejected — that's the current behaviour, and N-V1 demonstrated the multiplier exploit. Global pool closes it cleanly.
  • Mission-based redemption arc. Rejected — missions are not part of the canonical loop (faction reputation moves emergently via the action-delta table). Discrete actions in the action-delta table are the right shape.
  • Hard-block all multi-account clusters regardless of subscription tier. Rejected — punishes shared households and content creators with multiple personas. Subscription-tier gating preserves the legitimate use cases.
  • Skip account-tenure gate on voting. Rejected — even with multi-account detection, two weeks of "clean play" by an alt ring is feasible. 60 days raises the cost meaningfully.

Amendment A — Additive amendment per ADR-0091 (2026-06-28)

Status: Approved by Max (2026-06-27). Accepted as part of ADR-0091 (Accepted 2026-06-28).

Change: Extend MultiAccountDetectionService with a new settle_contest surface — a gated participation-weight query for the contestable-settle claim race introduced by ADR-0091:

  • eligible_for_contest(player_id, planet_id) → bool — returns True iff the player's account / registration / reputation scores clear the anti-sybil thresholds for contesting the specified planet.
  • Soft-flag (eligible but loses ties + forfeits loser relief) / hard-flag (blocked) semantics, consistent with the existing discount model.
  • No new account-linking logic — leverages existing detection surfaces. The once-per-account-lifetime comp flag remains ordinary player state (not an auth/MFA surface).
  • No loser relief between ADR-0056 linked accounts — prevents alt-feeding the loser-cost fund.

This ADR's accepted status and CI-enforcement are preserved; Amendment A is an additive method surface only, not a change to existing detection logic or the subscription-tier gating model.