Two things turn a server full of strangers into a place. The first is being able to hear the person standing next to you — to whisper a deal across a market stall, to key a radio and have only the dispatcher's net carry it, to take a phone call from across the map. The second is belonging — a crew with a name, a treasury, officers and recruits, a reputation that a whitelisted realm reads at its door, and an identity that survives when its members cross into another world entirely. V7 ("Mawu") names those two planes for two Greek goddesses: Pheme, the carrying, spreading voice, and Hera, goddess of alliance and organized society. This page is the feature tour of both.
Pheme is the positional, proximity-attenuated voice that immersive roleplay requires — the pma-voice model FiveM made the genre standard — layered with radio nets, phone calls, and the accessibility and child-safety guarantees the platform never delegates to a realm. Hera is the persistent player social graph: crews, guilds, and organizations as first-class, cross-realm, cross-version aggregates with role-gated treasuries, an audit ledger, a reputation whitelist gate, and an Ori-passport bridge that carries a group into V2–V6. Both are grounded in real, test-driven code, and this page follows the code where the prose runs ahead of it.
The authority that validates every Hera treasury debit and that hosts the radio nets lives in ./moremi-roleplay-framework.md; the way a crew travels into other Oshun games — and the streamer-mode redaction that protects a broadcasting player — lives in ./streaming-incarnation-and-live-service.md; the engineering companion that owns the data-model and voice-DSP detail is ../architecture/nana-data-model-and-pheme-voice.md. For the full V7 scope this slots into, start at the hub: ../V7_features.md.
What ships, honestly#
These two planes are real, test-covered code, but they ship at three different levels of assembly, and one of them — Pheme — is more implemented than the architecture companion's quick summary lets on. The honest picture:
- Real and test-covered. Pheme has two genuine evaluators. The
client-side mix is
UMawuVoiceMixLibrary::EvaluateVoiceEmitter(V7/ue/Source/MawuVoice/Private/MawuVoiceMix.cpp) — real UE C++ distance falloff, radio floor, and equal-power panning. The server-side audibility, admission, and captioning model is real Rust inapps/v7/mawu-gateway(src/lib.rs): proximity gain with ray-vs-AABB occlusion, scriptable whisper/normal/shout ranges, radio channel admission (PTT / squelch / role / on-duty), proximity-independent phone, a hearing-impaired caption projection, and a platform voice-safety tap that ignores a realm's request to disable it — four of the gateway's 16#[test]cases pin these. Hera is a real 1,085-line social graph (apps/v7/hera-social-service/src/lib.rs) with role-gated audited treasury withdrawals, a cross-realm reputation whitelist gate, an Ori group-presence passport with a continuity hash, and a platform-ban + device-attestation ban-evasion eval (7 tests). - Library-grade, not a running daemon. Both
mawu-gateway(owner "Mawu", port47204) andhera-social-service(owner "Hera", port47206) expose their behaviour as libraries plus a health-only daemon — eachmain()callsrun_service(), which binds the sharedservice_contracthealth server and answers one route. The audibility/admission model and the social graph are exercised by evals and tests, not by a live socket mixing Opus packets or serving group mutations. - Spec-level where it is genuinely reused or deferred. The codec and SFU
themselves — Opus over the WebRTC SFU — are the reused V6 Egbe-Gateway
transport, not re-implemented in V7; the gateway's own comment says "the SFU
transport itself (V6) is out of scope; this is the audibility/admission/
captioning model the SFU is driven from." The Pheme scale budget ("≤40
audible streams per listener" nearest-N AoI culling, "≤2 cores per 256
speakers", "p95 ≤250 ms mouth-to-ear") is a written launch target — the
audibility evaluator runs per speaker-listener pair, with no nearest-N
selection yet. And Hera group kinds are
Crew | Guild | Organization(the monolith's "gang"/"posse"/"org" are flavour, not enum variants).
Pheme — proximity and radio voice#
Two evaluators, one falloff model#
Pheme computes "what can this listener hear from that speaker" in two honest
places. On the client, the Mawu UE module ships
UMawuVoiceMixLibrary::EvaluateVoiceEmitter, a UBlueprintFunctionLibrary
(deps Core / CoreUObject / Engine only, per MawuVoice.Build.cs) that
takes an FMawuVoiceListener (location, forward, HearingRadiusCm default
3200, the RadioChannels it monitors) and an FMawuVoiceEmitter (location,
VoiceRadiusCm default 2400, SpokenLevelDb default −12,
bRadioTransmitting, RadioChannel) and returns an FMawuVoiceMixResult. The
math is real: the effective radius is min(HearingRadiusCm, VoiceRadiusCm), the
distance alpha is clamp(distance / radius, 0, 1), the falloff is
(1 − alpha)² (a smooth inverse-square-shaped curve), and the speech level
converts from decibels with 10^(clamp(SpokenLevelDb, −80, 12) / 20). Panning
is equal-power-shaped — the listener's right vector is cross(Up, Forward), the
pan is dot(directionToEmitter, right) clamped to [−1, 1] — and bAudible
trips only above a 0.001 linear-gain floor.
On the server, pheme_evaluate_audibility (mawu-gateway/src/lib.rs) does
the same falloff but adds two things the client evaluator does not: scriptable
voice modes and occlusion. PhemeVoiceMode gives whisper / normal / shout
distinct effective ranges of 300 / 1,800 / 5,000 cm; the distance gain is
the same (1 − alpha)² quadratic; and a PhemeOccluder (an axis-aligned box
with an attenuation_db) is tested by pheme_segment_hits_occluder, a real
ray-vs-AABB slab intersection along the speaker→listener segment. Each
occluder the segment crosses adds its dB, converted back to a linear factor as
10^(−occlusion_db/20), and the received level is
source_level_db + 20·log10(gain), floored at
PHEME_INAUDIBLE_FLOOR_DB = −120. The eval run_pheme_proximity_eval asserts
the properties that matter: gain is monotonically decreasing with distance,
a voice is silent beyond range, an occluded path is quieter than an
unoccluded one at the same distance, and whisper < normal < shout range
ordering holds.
Radio nets and phone calls#
Radio is a parallel, distance-independent path. On the client, when the
emitter transmits on a non-empty channel the listener monitors,
EvaluateVoiceEmitter floors the gain at 0.18 regardless of distance and
pulls the pan toward centre (Pan *= 0.25) — the familiar "in your head" radio
feel. On the server, pheme_evaluate_radio_transmit decides admission onto
a channel. PhemeRadioChannel carries an optional required_role, a
require_on_duty flag (for dispatch nets), and a squelch_threshold; the
result is a PhemeRadioAdmission that is one of Admitted, PttNotKeyed,
NotChannelMember, NotRoleAuthorized, NotOnDuty, or SquelchedWeakSignal.
The run_pheme_radio_phone_eval fixture is a police dispatch net: an on-duty
dispatcher is admitted, an off-duty dispatcher is NotOnDuty, a civilian is
NotRoleAuthorized, a released push-to-talk is PttNotKeyed, and a −110 signal
under a −90 squelch is SquelchedWeakSignal. Phone is
pheme_connect_phone_call — proximity_independent: true, point-to-point or
is_conference for more than two — and the eval connects two endpoints 50,000
cm apart (far beyond shout range) to prove distance never gates a call.
That dispatch radio net is also where Pheme meets the Ixchel audio-mod model in
the realm server. A police radio is declared as a
MoremiForgeAudioRoute("radio.police", "dispatch-radio", "voice")
(apps/v7/moremi-realm-server/src/lib.rs), and the forge-conflict resolver
keeps it on an independent mix bus from a music.club route ("club-music",
music) — while flagging a genuine dispatch-radio<->radio-hijack
contradiction when a hostile pack tries to seize the police channel onto the
wrong bus. Radio channels are not just a voice routing concept; they are a named
audio resource the composition layer protects.
Voice on the wire, and the safety tap the realm cannot turn off#
Voice negotiation is a first-class citizen of the realm protocol.
RealmWireMessageKind::VoiceSignal (libs/v7/realm-protocol/src/lib.rs) routes
onto the dedicated RealmWireChannel::Voice, is reliable-ordered (SDP
offers and answers cannot be dropped the way an AuthoritativeDelta can),
carries the wire label "voice-signal", and holds a distinct authority
scope, "realm.voice.signal", separate from the state-mutation scope. It is
one of the six required message kinds the wire-conformance harness drives
through reordering and loss, so the signalling path is held to the same framing
contract as movement. The substrate boundary names this seam explicitly:
substrate-bridge tags a "V6 realtime gateway and Pheme transport boundary" —
which is why no codec lives in the V7 crates.
Wrapped around the mix is a platform-owned safety tap the realm operator cannot
remove. MawuVoiceSafetyPolicy::platform_default() sets
platform_tap_required: true, realm_delegation_allowed: false, and a route
threshold of 7,000 basis points into the kuanyin-human-review queue.
screen_voice_segment scores a segment on direct-audio-grooming, coercive-tone,
distress, and minor-context signals, and — critically — when a realm sets
realm_requested_tap_disabled, the decision records
realm_disable_request_ignored: true and screens anyway. This is the
trust-boundary posture in miniature, proven by
platform_voice_safety_tap_routes_flagged_segments_and_ignores_realm_disable.
Child-safety reaches further: Sekhmet makes ProximityVoice a
MinorProtectionFeature (apps/v7/sekhmet-scanner/src/lib.rs) and screens a
VoiceGroomingSignal endangerment kind on a tight 2,400 ms SLA.
Accessibility parity#
No voice surface ships without a text/visual equivalent. On the server,
pheme_build_accessibility_view projects live PhemeVoiceEvents into a
PhemeAccessibilityView: for a hearing-impaired profile with captions on, every
event becomes a caption line carrying who (speaker), which channel
(proximity / radio / phone + id), and where (distance + direction for
proximity, "off-proximity" for radio/phone), plus distinct speaker and channel
indicators. A parental_mute profile suppresses all surfaced voice and
emits no captions at all. The contract layer enforces the same bar statically:
lintVoiceRadioParity (libs/v7/contracts/src/accessibility-eval.ts) requires
captions, transcript, and speaker / range / channel / mute-state visual
indicators for both proximity and radio voice, emitting
voice_radio_parity_missing:<field> against the
voice-radio-text-visual-parity rule when a surface omits one. The mix
evaluator decides what you hear; the accessibility view and the parity linter
decide what the platform must guarantee around it.
Hera — crews, guilds, and the persistent social graph#
The group aggregate#
A Hera group is a real aggregate, not a join table. HeraGroup carries a kind
(Crew | Guild | Organization), a home_realm_id, a map of HeraGroupRoles
(each a permission set drawn from TreasuryDeposit, TreasuryWithdraw,
SharedAssetManage, ReputationRead), members with their assigned role_ids,
a HeraTreasuryAccount (an aje_account_id plus a balance_cents), shared
assets, and a cross_realm_reputation_score. Everything a group can do is
mediated by actor_has_permission, which walks a member's roles and checks the
requested permission — there is no "owner can do anything" shortcut.
Role-gated, audited treasury#
The treasury is the load-bearing example. withdraw_from_treasury checks
actor_has_permission(TreasuryWithdraw) and then either denies with
missing_treasury_withdraw_permission / insufficient_treasury_funds or debits
the balance — and every attempt, permitted or not, appends a sequenced
HeraGroupAuditEntry recording the actor, action, decision, reason, amount, and
the treasury balance after. publish_shared_asset is gated the same way on
SharedAssetManage. The gate run_hera_groups_eval only passes when a
permissionless member's 2,500-cent withdrawal is denied (the 25,000-cent
balance untouched), a treasurer's 7,500-cent withdrawal succeeds (balance →
17,500), a quartermaster's shared-asset publish succeeds, and the audit
ledger shows at least two permitted and one denied entry — proven by
groups_eval_grants_role_gated_treasury_and_cross_realm_reputation. The
treasury balance is in aje_account_id terms, so a group's shared money is
settled through Aje, never minted inside the social service.
Cross-realm reputation and the whitelist gate#
Reputation is the thing a group earns in one realm that another realm reads at
its door. evaluate_cross_realm_whitelist_gate compares a group's
cross_realm_reputation_score against a target realm's required_minimum_score
and returns a HeraGroupWhitelistDecision — in the eval, a score of 82
clears a required 50, so an established crew is admitted to a whitelisted RP
realm without re-vetting every member. A group is also a governance tier:
the Eunomia service (apps/v7/eunomia-governance-service/src/service.ts) makes
'guild' a first-class EunomiaGovernanceTier whose parent is 'server', with
reputation-weighted voting, so a guild's charter and treasury decisions run
through the same governance plane as the realm above it.
Group presence travels — the Ori passport#
The "a crew travels into V2–V6" promise is implemented, not asserted.
mint_ori_group_presence_passport snapshots each member's role_ids into a
HeraOriGroupPresencePassport and stamps a continuity hash — a
deterministic FNV-1a fold over the group id and the per-account role assignments
(stable_group_presence_hash, formatted fnv64:…).
bridge_group_presence_passport_to_v2_match then projects that passport into a
HeraV2MatchGroupRoster, recomputing the same hash so any tamper is
detectable. run_hera_cross_version_group_presence_eval passes only when the V2
roster preserves the member count, every role assignment, and a matching
continuity hash — group identity that survives the version boundary, the Hera
analogue of the Ori passport the substrate bridge mints for individual
characters. A "Moonlit Couriers" crew formed in a market realm can therefore
field a verified two-runner roster in a V2 ranked match with its roles intact.
Ban propagation and device-attestation evasion#
Hera closes the smurf / ban-evasion loop FiveM left open, and it is the platform
— never the realm — that owns it. PlatformBanLedger records a PlatformBan
keyed on both a platform_account_id and a device_attestation_id, and
evaluate_realm_join returns a RealmJoinDecision that blocks not only the
banned account (BlockedPlatformAccountBan) but a fresh account joining from
the same attested device (BlockedDeviceAttestation). A ban also drives the
cross_realm_reputation_score negative. run_ban_evasion_eval passes only when
both the original account and a fresh same-device account are blocked,
propagation lands within V7_BAN_PROPAGATION_SLA_MS (5,000 ms — the fixture
lands in 2,000), and the reputation score is negative — pinned by
device_attestation_blocks_fresh_account_realm_hop and
ban_evasion_eval_blocks_platform_ban_and_device_hop. This is the trust
boundary's "platform owns identity and bans, realms cannot launder them" posture
made concrete in the social graph: a banned player cannot simply re-register and
realm-hop their way back in.
Where this connects#
Pheme and Hera are the payload the realm backbone carries: a voice signal is a reliable-ordered frame on the realm wire, and every treasury debit is a server-authoritative mutation. The authority that validates those writes, hosts the radio nets, and owns the Nàná characters those voices belong to is ./moremi-roleplay-framework.md. The way a Hera crew incarnates into V2–V6 via the Ori passport — and the streamer-mode redaction that protects a broadcasting voice — lives in ./streaming-incarnation-and-live-service.md. The data-model and voice-DSP engineering behind everything on this page, including the character record and economy ledger these social and voice planes sit beside, is in the architecture companion ../architecture/nana-data-model-and-pheme-voice.md. For the full V7 feature scope, return to the hub: ../V7_features.md.
Related#
- Moremi: The Server-Authoritative Roleplay Framework — the authority that validates every Hera mutation and hosts the Pheme radio nets
- Streaming, Incarnation & Live Service — how a crew travels into V2–V6 and how streamer-mode protects a live player
- Nàná Data Model, Pheme Voice & Hera Social Graph — the engineering companion: character/economy data model and the voice-DSP math
- The feature hub: ../V7_features.md