NetworkSubjectMergedPayload network.subject.merged@1

Generated at from openvibe-contracts v0.76.0 and openvibe-sdk v0.20.1.

Version
1.0.0
Owner
network
Visibility
first-party
Status
active
Compatibility
backward
Decision
ADR-029
Schema
https://openvibe.network/contracts/events/payloads/network.subject.merged.v1.json

network.subject.merged v1 (OpenVibe.Network; roadmap WS-B task 5, ADR-029). Two accounts became one: `from`, the folded-in subject, is now an alias of `into`, the survivor, which keeps its subject, username, profile and settings. Network has already moved what it owns (linked providers, sessions, OAuth grants, developer projects, OpenCoins and user modules) in the transaction that writes this event to its outbox. Every service that stores subjects repoints its own rows from `from` to `into` in its own transaction and keeps no copy under `from`; a row it cannot move (a unique conflict, such as both accounts following the same channel) keeps the survivor's row and drops the other, as a counted, logged outcome. Until a service has applied it, its rows stay under `from`, which still resolves to `into` through Network's aliases. Apply once per merge_id. Envelope: subject { type: user, id: <into> }, visibility internal, actor the person who merged (or the staff member, for an account-recovery merge).

Fields

FieldTypeRequiredDescriptionConstraints
merge_idstringyesThe merge; a redelivery carries the same id.
  • pattern ^mrg_[0-9A-HJKMNP-TV-Z]{26}$
fromstringyesThe folded-in subject, now an alias.
  • pattern ^usr_[0-9A-HJKMNP-TV-Z]{26}$
intostringyesThe survivor.
  • pattern ^usr_[0-9A-HJKMNP-TV-Z]{26}$
merged_atstringyes
  • format date-time
initiated_byenumyesperson: the account holder, signed in to both accounts. staff: an account-recovery merge (staff.identity.merge, with a written reason and an audit row).
  • one of "person", "staff"
split_untilstringUntil then Network keeps the folded-in account's pre-merge state, so staff can split a mistaken merge by hand (30 days).
  • format date-time

Examples

From the contract's own test fixtures: valid ones validate, rejected ones must fail.

Valid: person
{
  "merge_id": "mrg_01JAB2C3D4E5F6G7H8J9K0MNPS",
  "from": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR",
  "into": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "merged_at": "2026-09-27T21:00:00.000Z",
  "initiated_by": "person",
  "split_until": "2026-10-27T21:00:00.000Z"
}
Valid: staff
{
  "merge_id": "mrg_01JAB2C3D4E5F6G7H8J9K0MNPS",
  "from": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR",
  "into": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "merged_at": "2026-09-27T21:00:00.000Z",
  "initiated_by": "staff"
}
Rejected: leaks-username
{
  "merge_id": "mrg_01JAB2C3D4E5F6G7H8J9K0MNPS",
  "from": "usr_01JAB2C3D4E5F6G7H8J9K0MNPR",
  "into": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "merged_at": "2026-09-27T21:00:00.000Z",
  "initiated_by": "person",
  "username": "bob"
}
Rejected: legacy-id
{
  "merge_id": "mrg_01JAB2C3D4E5F6G7H8J9K0MNPS",
  "from": "42",
  "into": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "merged_at": "2026-09-27T21:00:00.000Z",
  "initiated_by": "person"
}

Validate

const contracts = require('openvibe-contracts');
contracts.validate('network.subject.merged@1', value);   // { valid, errors: [{ path, message }] }