Skip to content

Police Forces

Status: ๐Ÿšง Partial โ€” named officer roster, engagement routing, and Interdictor hull enforcement partially shipped; escort NPCs, cargo confiscation depot, and several Sentinel barracks mechanics remain design-only. (re-verified 2026-08-21 vs Sectorwars2102 46bce720.)

Two NPC-only enforcement bodies patrol the universe's protected spaces. Each is staffed by named individual NPCs of the LAW_ENFORCEMENT archetype within the wider NPC lifecycle framework โ€” real persistent characters with single-place presence (one sector at a time), full daily schedules (8h patrol / 8h off-duty / 8h sleep), career progression (recruit โ†’ active โ†’ senior โ†’ decorated โ†’ retirement / KIA / succession), names players come to know, and identities that survive across encounters. Each force flies a special-issue Interdictor hull that no player can ever own โ€” the Federation Marshal Interdictor for the Federation Police, the Nexus Sentinel Interdictor for the Sentinel Corps. Their job is to give the universe's positive-aligned spaces real teeth โ€” Wanted Status, contraband detection, and protected-sector invariants stop being theoretical the moment a named Marshal or Sentinel arrives and intercepts.

Officers aren't anonymous squad slots and they aren't 24/7. Marshal Cassandra Vance patrols sectors 12โ€“47 of your home region from 06:00โ€“14:00 UTC; she's off-duty at the Capital station from 14:00โ€“22:00, and asleep from 22:00โ€“06:00. If you commit a crime in sector 47 at 03:00 UTC, she's not there โ€” engagement-routing dispatches whoever is on shift. Sentinel-Captain Lyra Kett stands guard in Gateway Plaza on a different shift schedule that overlaps Vance's by design. Each region has its own roster of named officers across staggered shifts so coverage is continuous.

The data model lives in ../../DATA_MODELS/npcs.md; the lifecycle framework (schedules, career arcs, succession) lives in ../../SYSTEMS/npc-lifecycle.md; the scheduler that moves NPCs between sectors and routes engagement responses is in ../../SYSTEMS/npc-scheduler.md. Police are one specific archetype within that framework; pirates, station officials, traders, faction leaders, and other NPC types use the same lifecycle layer with different schedule templates and behavior trees.

Two forces at a glance

Force Jurisdiction Affiliation Mandate Special-issue hull
Federation Police Federation Zone of every region (sectors 1..floor(0.33 ร— total_sectors)) and all of Terran Space Terran Federation Enforce Wanted Status, contraband interdiction, attacks-on-innocents response Federation Marshal Interdictor
Nexus Sentinel Corps Central Nexus (entire region, all 5,000 sectors) Galactic Concord (operator-managed authority; not a player-targetable faction) Protect Nexus governance, prevent warp-gate construction in protected sectors, intercept hostile inbound traffic, defend the Capital Nexus Sentinel Interdictor

Both forces are positive-aligned (lawful, not adversarial-by-default) โ€” they leave compliant players alone. Players who keep a clean rep, dock with valid pins, and route around protected-sector restrictions never see one. Players who cross a line see the squad arrive, the Interdictor field engage, and a real combat encounter that ends with arrest, fine, or destruction depending on severity.

The two forces never share jurisdiction: a player in the Federation Zone of a player-owned region sees Federation Police only; a player in the Central Nexus sees Sentinels only. The two never coordinate operations and don't share intelligence โ€” Federation Police records of a player's Wanted Status do not propagate into Nexus Sentinel pursuit (and vice versa). This is intentional: Federation enforcement is about player-level alignment; Nexus enforcement is about hub-level invariants.

Federation Police

Jurisdiction

Operates in the Federation Zone of every region (the first 33% of sector numbers per ../galaxy/sectors.md) plus the entirety of Terran Space (all 300 sectors are effectively Federation-policed at policing 9). The starter cluster (sectors 1..fedspaceSize, default 10) is the densest patrol coverage. Border and Frontier zones are out of jurisdiction โ€” Federation Police never spawn there.

Engagement triggers

A Federation Police squad spawns and engages a player when any of the following fires inside its jurisdiction:

  • Wanted Status active โ€” the reconciled union trigger set ratified by ADR-0093 item 39 (folded from ADR-0093, re-verified 2026-08-07; the ADR's own citation of "ADR-0007" as part of the existing trigger set was a bug โ€” the actual source is ranking.md ยง177-211, corrected there and in the ADR itself) per ./ranking.md#wanted-status: Player.personal_reputation < โˆ’500 (Villain / Criminal tiers on the personal-alignment scale โ€” not Outlaw, which sits above โˆ’500, and not Pirate / Public Enemy, which are faction-ReputationLevel names) or an active stolen-ship report on the player's current ship per SYSTEMS/ship-registry.md. This doc's engagement rules (squad dispatch, spawn delay, jurisdiction) govern the police RESPONSE to a Wanted player; ranking.md governs the flag's other consequences (name color, bounty, reputation drain, port impound).
  • Attack on an innocent โ€” an attack_innocent reputation trigger fires (target had no bounty, defender was not Wanted) per ./factions-and-teams.md. The squad arrives within 2 turns of the attack resolving โ€” see Engagement dispatch and the 2-turn spawn delay below.
  • Contraband detected โ€” the player carries hazardous_transport cargo (per ../economy/black-market.md) and is scanned by a passing patrol. Detection probability scales with the player's Ship.evasion (high evasion = lower detection rate).
  • Protected-sector incursion โ€” the player breaches a Federation-protected sector with hostile intent (e.g., attempts to attack a Federation-flagged station's docked ships).

The squad does not engage on a Wanted player who keeps moving without committing a new offense โ€” Wanted Status is the predicate for engagement, not a one-shot bounty trigger. The squad spawns when an offense fires, pursues until the player exits Federation jurisdiction, is destroyed, or surrenders.

Engagement dispatch and the 2-turn spawn delay

When an offense fires, engagement-routing commits the responding officer immediately but the squad's arrival is gated on the offending player consuming 2 turns. Delay is measured in the offender's own turn consumption โ€” turns spent on movement, combat, or trade โ€” not wall-clock time, so the chase stays in the game's native time unit. This gives the offender a real tactical window between the offense and the intercept.

Dispatch proceeds in two phases:

  1. Commit (at offense time). The router picks the eligible Marshal(s) per the routing rules (nearest-by-warp-hop, squad composition per offense severity) and atomically flips them to engaged_pending_arrival. Their current_sector_id does not change yet. A PendingEngagement row records (player_id, offense_sector_id, npc_squad_ids[], offense_at_turn_count, arrival_turn_threshold = offense_at_turn_count + 2) (โœ… models/pending_engagement.py + npc_engagement_service.route_engagement + engagement_summary(); origin/feat 46bce720). ๐Ÿšง En-route countdown banner โ€” tip-absent. The player client does not render a "police en route" countdown on tip (git grep en route / PendingEngagement / turns to arrival under services/player-client/src is empty). The engagement_summary() example copy ("Marshal Vance is en route โ€” 2 turns to arrival") is gameserver payload / canon illustration, not player-live HUD โ€” do not read this sentence as a named-officer product requirement. Consolidated player-client split: ยง Player-facing affordances.
  2. Arrival (after 2 turns). When the player's cumulative_turn_count reaches arrival_turn_threshold, the squad spawns in whatever sector the player is currently in โ€” not the offense sector. This is the canonical "the police chase you" semantic: the squad's current_sector_id is set to the player's current sector, status flips to engaged, and the squad is handed to the combat resolver.

The engaged_pending_arrival status is distinct from engaged so a committed-but-not-yet-arrived Marshal is excluded from new offense responses โ€” a Marshal already pending an arrival cannot be picked for a second offense; that offense falls through to the per-player cooldown or to a different officer.

A per-player offense cooldown of 5 turns applies between accepted offenses of the same type (attack_innocent, contraband_scan_hit, etc.). Offenses inside the cooldown still fire reputation hooks but do not dispatch a new squad โ€” the existing pending or active squad's pursuit covers them. Different offense types stack normally, each with its own dispatch and its own per-type cooldown. This prevents griefing the routing system by stacking offenses faster than squads can arrive.

Jurisdiction exit before arrival. If the player exits Federation jurisdiction before the threshold is reached, the engagement resolves immediately on the boundary cross as jurisdiction_exit: the committed Marshals revert to on_duty, the PendingEngagement row is deleted, and personal reputation drops by โˆ’25 for evading arrest (see Engagement outcomes). Fleeing across a region boundary within 2 turns is a viable escape with this real reputation price.

Disconnect and expiry. If the player disconnects, the turn watcher pauses and the PendingEngagement row persists; on reconnect the watcher resumes from the player's then-current turn count. A pending engagement that sits idle for more than 24 hours of wall-clock time expires โ€” the held Marshals release back to on_duty and a fresh squad spawns on the next offense. The 24-hour cap also bounds the worst case for a player who consumes turns very slowly.

PendingEngagement is a durable table row, not an in-memory queue, so dispatches survive scheduler restarts. The scheduler's Loop A sweeps pending rows to discharge any whose arrival threshold is reached, clear any whose player has exited jurisdiction, and expire any stale enough to release their held Marshals.

Roster, lodging, and squad composition

Each non-Nexus region has a permanent roster of named Federation Marshals maintained by the NPC scheduler:

  • 8 Marshals + 1 Marshal-Captain per Standard-tier player region (target counts in NPCRoster; tunable per region size).
  • 12 Marshals + 2 Marshal-Captains in Terran Space.
  • Lodging: a single NPCBarracks row at the region's Capital station; capacity 9 (8 Marshals + 1 Captain). Station.services.npc_barracks = true flags the Capital as a barracks host. Marshal-Captains who are promoted out of the shared barracks (๐Ÿ“ future) get a private-quarters NPCBarracks row (capacity 1) at the same station.
  • Ship parking: each Marshal has a permanent 1:1 Marshal Interdictor assignment. When off-duty, the Interdictor docks at the Capital station and lands in Sector.defenses.docked_npc_ships with status = "docked_off_duty" โ€” shielded from player attack while there. Auto-maintenance during sleep blocks repairs the hull to 90% if it dropped below 75%.
  • Shift overlap: consecutive Marshal shifts overlap for 30 minutes so the outgoing officer can brief the incoming officer (shift_handoff_state JSONB on the squad row passes threat notes about active investigations / Wanted players / pending pursuits). During the overlap, both Marshals are visible in the same patrol-route sectors with current_activity = shift_handoff.

Each Marshal is a persistent NPCCharacter row โ€” name, title, backstory, current sector, assigned Marshal Interdictor ship. They patrol on a 4-hour-per-sector cycle through 4โ€“6 sectors of the region's Federation Zone, with ~20% off-duty rotation at any given time. Players in the Federation Zone may encounter the same Marshal repeatedly across sessions; engagement-routing picks the nearest available Marshal (not a fresh anonymous squad). Killing a Marshal removes that named individual from the roster permanently. Per ADR-0063 N-D2, the scheduler's roster-maintenance loop spawns a new named successor Marshal immediately โ€” there is no vacancy. The successor enters at reduced-stat RECRUIT stage and reaches full ACTIVE strength after a 7-canonical-day tenure.

Squad composition for an engagement scales with the offending player's threat tier and dispatches the named Marshals in pursuit:

Threat tier Player rep tier Squad composition
Low Suspicious / Questionable 1 named Marshal
Medium Smuggler / Outlaw 2 named Marshals (nearest two)
High Pirate / Criminal 3 named Marshals + 1 NPC Defender escort
Public Enemy Public Enemy 3 named Marshals + 2 NPC Defender escorts

The named threat tiers above are a descriptive shorthand. The decision variable the engagement router actually keys on is the player's personal alignment (Player.personal_reputation), graded by the 8-tier REPUTATION_TIERS scale in services/gameserver/src/services/personal_reputation_service.py (Villain / Criminal / Outlaw / Suspicious on the negative side) โ€” not the separate 17-level per-faction ReputationLevel enum (services/gameserver/src/models/reputation.py) whose names (Questionable, Smuggler, Pirate, Public Enemy) the threat-tier column borrows. The two are distinct systems: personal alignment is a single signed score per player; ReputationLevel is a player's standing with one faction. Police squad scaling reads personal alignment only.

_federation_squad_size in services/gameserver/src/services/npc_engagement_service.py maps personal_reputation onto squad size at three thresholds, which canonically reconcile the named threat tiers to the live alignment bands as follows:

Threat tier personal_reputation band REPUTATION_TIERS name Named Marshals Captain joins
Low โˆ’249 โ€ฆ โˆ’1 (and โ‰ฅ 0) Suspicious / Neutral+ 1 no
Medium โˆ’499 โ€ฆ โˆ’250 Outlaw 2 no
High โˆ’749 โ€ฆ โˆ’500 Criminal 3 no
Public Enemy โ‰ค โˆ’750 Villain 3 yes

So "Smuggler / Outlaw" (Medium) corresponds to the Outlaw band crossing โˆ’250; "Pirate / Criminal" (High) to the Criminal band crossing โˆ’500; and "Public Enemy" to the Villain band crossing โˆ’750, which is also the only tier where the Marshal-Captain auto-joins (include_captain). The Wanted-Status engagement predicate in the trigger list above (personal_reputation < โˆ’500) sits at the High/Public-Enemy boundary on this same scale.

The escort NPCs in the table (๐Ÿ“ Design-only) are not yet dispatched โ€” route_engagement commits only named officers today, so a High or Public-Enemy squad arrives as 3 (or 3 + Captain) named Marshals with the escort hulls flagged as a later slice. The escort NPCs (Defender / Light Freighter NPCs) are anonymous fleet support โ€” only the Marshals carry persistent identity. The Marshal-Captain is the regional head; they personally respond to Public Enemy tier engagements and to any direct attack on a Marshal.

If every Marshal in the region is engaged, off_duty, or KIA, response falls back to the scheduler's regional grace window (5โ€“15 minutes before the next available Marshal arrives) โ€” see ../../SYSTEMS/npc-scheduler.md#engagement-routing. This is the canonical answer to "what if a player wipes out the Federation Police?" โ€” they get a small window of effective lawlessness, then a new named Marshal takes over.

Engagement outcomes

When a Federation Police squad catches a player, three resolutions are possible:

  1. Surrender โ€” player declines combat, accepts a credit fine (~10โ€“25% of cargo value, scaled by offense severity) plus a +5 personal reputation nudge for compliance, and is released. The arrest is logged.
  2. Successfully flee โ€” player escapes via Slipdrive, Quantum Jump, exiting the Federation Zone boundary, or destroying the squad. Personal reputation drops further (โˆ’25 for "evade arrest"). Squad de-spawns; a fresh squad respawns on next offense.
  3. Destruction โ€” squad wins the combat. Player is destroyed normally per the canonical destruction handler. The player's cargo is confiscated to a Federation depot (๐Ÿ“ Design-only โ€” recoverable via reputation rehabilitation), insurance pays out per InsuranceType rules.

Federation Police squads do not issue bounties on themselves and are not eligible bounty-collection targets. Killing a Marshal Interdictor crashes the player's Federation reputation by โˆ’250 per kill and immediately escalates the response squad.

Nexus Sentinel Corps

Jurisdiction

Operates in the Central Nexus exclusively. All 5,000 Nexus sectors are policed; the Gateway Plaza cluster (containing the Nexus Capital at sector 2551) carries the densest patrol coverage.

The Sentinel Corps does not operate in player-owned regions, Terran Space, or the Federation Zones of any player region โ€” that's Federation Police's job. A player who flees a Sentinel pursuit by warping into a player region exits Sentinel jurisdiction (the squad does not pursue across the Nexus boundary).

Engagement triggers

The Sentinel mandate is hub-level invariant protection, not personal-rep policing. Triggers:

  • Warp-gate Phase 1 deployment in a protected Nexus sector. This is the load-bearing case. Phase 1 of warp-gate construction (POST /api/warp-gates/deploy-beacon per ../galaxy/warp-gates.md) in any Nexus sector flagged is_nexus_protected = true (โœ… Shipped column, default true for the Capital sector 2551 and all sectors in the Gateway Plaza cluster, plus operator-configured additions) triggers immediate Sentinel response. The beacon deployment is rejected at the API layer โ€” but the spawn fires anyway, so a player who attempts the deployment via direct DB manipulation or finds a validation bypass is intercepted.
  • Hostile combat in protected Nexus sectors. Any combat initiation against another player or a Nexus station inside a is_nexus_protected sector spawns a Sentinel squad regardless of the attacker's rep tier.
  • Contraband transit through the Nexus Capital. Same detection model as Federation Police โ€” probability scales with Ship.evasion.
  • Capital-sector incursion at Gateway Plaza. Any attempt to attack the Capital welcome planet (the TERRA planet at sector 2551 per ADR-0014) or to dock at Starport Prime with a Wanted-Status pilot.
  • Smuggling Quantum Crystals in bulk through the Nexus (๐Ÿ“ Design-only) โ€” bulk-Crystal transit is treated as preparation for unsanctioned gate construction and triggers a stop-and-search.

The Sentinel Corps does not engage on the standard Wanted Status reputation tiers โ€” a player can be Public Enemy under Federation rep and still transit the Nexus unmolested as long as they don't trigger one of the above hub-invariant triggers. (Federation Police can't pursue across the Nexus boundary, and Sentinels don't enforce Federation rep โ€” so the Nexus is a partial sanctuary for Wanted players who behave themselves while inside.)

Roster, lodging, and squad composition

The Central Nexus has a permanent Sentinel Corps roster maintained by the NPC scheduler:

  • 24 Sentinels + 4 Sentinel-Captains in the Nexus (a single 5,000-sector region needs proportionally more officers than a player region).
  • Lodging: a dedicated NPCBarracks row in a Gateway Plaza cluster sector โ€” the Sentinel barracks sector โ€” separate from Starport Prime so player traffic at the Capital isn't crowded by 28 docked Interdictors. The galaxy generator's Phase 12.5b sets Sector.is_npc_barracks_sector = true on this sector. Capacity 28; advisory (overflow logged).
  • Ship parking: each Sentinel has a permanent 1:1 Sentinel Interdictor assignment. When off-duty, the Interdictor docks at the Sentinel barracks sector and lands in Sector.defenses.docked_npc_ships with status = "docked_off_duty" โ€” shielded from player attack while there. Auto-maintenance during sleep blocks repairs the hull to 90% if it dropped below 75%.

Each Sentinel is a persistent NPCCharacter row patrolling a 3-hour-per-sector cycle through 4โ€“6 Gateway Plaza or adjacent-cluster sectors, with off-duty rotation at the Sentinel barracks (๐Ÿ“ Design-only โ€” a designated rest sector in the Gateway Plaza cluster). Sentinel-Captains rotate through Gateway Plaza and the high-traffic clusters (Commerce Central Hub, Diplomatic Quarter).

A Sentinel response is always 4 named Sentinels dispatched by the scheduler. The dispatch picks the nearest 4 on-duty Sentinels in the Nexus by warp-graph hop distance from the offense sector. There is no scaling table โ€” the Sentinel Corps treats every protected-sector violation as the same severity (the trigger itself is the severity grade).

If a Wanted-Status player attempts a warp-gate Phase 1 deployment in the Nexus Capital sector, the engagement routing dispatches Sentinel-Captain Lyra Kett (or whoever currently holds the Captain role) plus the 3 nearest Sentinels โ€” the Captain personally handles every Capital-sector breach.

Killing a Sentinel removes the named individual permanently. Per ADR-0063 N-D2, the scheduler's roster-maintenance loop spawns a new named successor Sentinel immediately โ€” there is no vacancy. The successor enters at reduced-stat RECRUIT stage and reaches full ACTIVE strength after a 7-canonical-day tenure. An escalated response (4 โ†’ 6 Sentinels) dispatches the standard 4, plus the next 2 nearest Sentinels who would have been on patrol โ€” 6 named Sentinels total, drawn from the live roster.

Engagement outcomes

Same three-outcome model as Federation Police (surrender / flee / destruction), with one critical difference: a Sentinel intercept of a warp-gate Phase 1 deployment is non-negotiable. The player cannot surrender the deployment โ€” the squad's mandate is to prevent the gate from existing, not to fine the player. The Phase 1 materials (turns, credits, ORE, EQUIPMENT, Crystal) are sunk. The player can still surrender the combat afterward (and pay a fine) or attempt to flee, but the gate construction is over the moment the Sentinels arrive.

This is the canonical answer to "what stops a player from anchoring a warp gate at the Nexus Capital": the Sentinel Corps. The Phase 1 API rejection is the first line of defense; the Sentinel intercept is the backup that makes the rejection load-bearing even if the validation has a bug or bypass.

Killing a Sentinel Interdictor crashes the player's standing with the Galactic Concord by โˆ’200 reputation per kill (operator-managed faction; not in the allyable 8-faction list โ€” โœ… Faction row seeded, CONCORD faction row seeded and the โˆ’200-per-kill figure ratified by ADR-0093 item 38, folded from ADR-0093, re-verified 2026-08-07) and triggers an escalated Sentinel response (4 โ†’ 6 Interdictors). Players cannot become friendly with the Galactic Concord โ€” it has no positive reputation triggers. Negative standing only.

Interdictor hulls

Both ships are NPC-only special-issue hulls. They do not appear in the player-facing ship roster (ship-roster.md). Players cannot purchase, capture, salvage, or claim them โ€” destruction creates no Cargo Wreck, and disabled-but-intact Interdictor abandonment is technically possible but the registry rejects player ownership transfer with ERR_NPC_ONLY_HULL.

Both Interdictors carry a unique Interdictor Field ability: when the field engages, the targeted player ship's WarpTunnel.turn_cost reads are clamped to infinity for 3 combat rounds. The targeted ship cannot warp out, cannot Quantum Jump (cooldown still ticks down but commit is rejected), and cannot use a player warp gate. The Interdictor Field also disables Slipdrive for the duration. The targeted ship can still fight, surrender, or attempt to escape via sector-edge proximity โ€” but the easy outs are closed.

The Field is single-target per Interdictor โ€” a 3-Marshal squad can pin three different ships, but a single ship is only pinned by one Field at a time (no stacking).

Federation Marshal Interdictor

Stat Value
Hull 1,200
Shields 800
Speed (current_speed) 1.5
Evasion 35
Scanner range 6
Attack rating 35
Defense rating 50
Default weapon Laser
Special abilities Interdictor Field (3-round target lock); Contraband Scanner (probabilistic detection of hazardous_transport cargo)
Hull class NPC-only npc_marshal_interdictor (extends ShipType enum); never serialized to player-facing ShipType lists

The Marshal Interdictor is built for sustained pursuit through Federation Zone sectors. It outguns a Defender in 1v1 combat but loses to a Carrier; its design intent is "police squad backed by Federation Navy when needed", not "fleet flagship." The Contraband Scanner triggers on every sector entry while in jurisdiction; detection probability per scan is 0.3 / max(1, target.evasion / 10).

Nexus Sentinel Interdictor

Stat Value
Hull 1,500
Shields 1,000
Speed (current_speed) 1.5
Evasion 40
Scanner range 7
Attack rating 40
Defense rating 60
Default weapon Plasma
Special abilities Interdictor Field (3-round target lock); Beacon Disruptor (cancels in-progress warp-gate Phase 1 within 1 sector on engagement); Concord Authorization (no toll on any warp gate, no whitelist requirement)
Hull class NPC-only npc_sentinel_interdictor (extends ShipType enum); never serialized to player-facing ShipType lists

The Sentinel Interdictor is tougher than the Marshal โ€” its mandate is hub-level governance, and the squad must hold the line even against well-equipped player gate-builders sacrificing Warp Jumpers. The Beacon Disruptor is the unique signature ability: it cancels an in-progress warp-gate Phase 1 deployment in the engaged sector or any adjacent sector, refunding nothing to the player. Combined with the Interdictor Field, a single Sentinel can stop a gate-build attempt cold. The squad of 4 ensures the response is overwhelming even if one Interdictor is destroyed.

The Concord Authorization is a quality-of-life detail: Sentinels never pay tolls on player warp gates and never trigger access-control rejections, so a Sentinel pursuit cannot be evaded by ducking through a gate the Sentinels would normally be barred from.

Schema impact

Named-NPC integration

Both forces use the named-NPC schema at ../../DATA_MODELS/npcs.md. Each Marshal and Sentinel is a persistent NPCCharacter row with a stable identity, current sector, and assigned ship. The runtime scheduler (../../SYSTEMS/npc-scheduler.md) handles patrol movement, engagement routing, off-duty rotation, KIA processing, and roster maintenance.

Roster targets per region (in NPCRoster):

Region kind Faction Role Target count
Player-owned Terran Federation marshal 8
Player-owned Terran Federation marshal_captain 1
Terran Space Terran Federation marshal 12
Terran Space Terran Federation marshal_captain 2
Central Nexus Galactic Concord sentinel 24
Central Nexus Galactic Concord sentinel_captain 4

Patrol-squad row coherence

โœ… Shipped, under a different key than originally specified here. This section originally described squad rows landing in a unified Sector.defenses.patrol_ships list. The actual shipped mechanism (src/services/npc_spawn_service.py, src/services/movement_service.py) instead uses two separate dedicated keys under Sector.defenses: police_patrol_ships (Federation Marshal / Nexus Sentinel squads) and pirate_patrol_ships (ADR-0047 pirate rosters). patrol_ships itself is never written by this mechanism.

Why the split: patrol_ships is also a key on the unrelated Station.defenses JSONB blob (a per-station scalar-int garrison count, see ../../DATA_MODELS/jsonb-schema.md "Station.defenses") โ€” the two never actually collide at runtime (different models), but the split-key choice was made defensively under that belief and is now the shipped, tested mechanism. Unifying onto a single patrol_ships list would require rewriting the live encounter/pursuit-dispatch engine (movement_service.py's sector-entry check, npc_engagement_service.py's ADR-0042 route/sweep) โ€” not attempted here; this doc is corrected to describe what ships today.

Each entry in police_patrol_ships / pirate_patrol_ships:

{
  "patrol_id": "<uuid>",
  "faction_code": "terran_federation" | "galactic_concord",
  "squad_kind": "federation_marshal" | "nexus_sentinel",
  "npc_character_ids": ["<npc-uuid-1>", "<npc-uuid-2>", "<npc-uuid-3>"],
  "ship_count": 3,
  "wanted_threshold": -500,
  "deployed_at": "<iso8601>",
  "scheduled_clear_at": null
}

The scheduler maintains a hard invariant: every NPC in npc_character_ids has current_sector_id matching the row's parent sector. When a Marshal moves on patrol, the scheduler atomically updates both the prior sector's squad row and the new sector's squad row, plus the per-NPC current_sector_id. Players see the Marshal arrive via realtime npc_arrived events.

squad_kind accepts the values federation_marshal and nexus_sentinel โ€” the canonical patrol-kind enumeration in DATA_MODELS/jsonb-schema.md is extended accordingly.

Sector protection flag

A new boolean column Sector.is_nexus_protected (โœ… Shipped) flags Nexus sectors that trigger the Sentinel response on warp-gate Phase 1 deployment. Default false. The galaxy generator's Phase 8 (long-distance warp tunnels) is extended to set is_nexus_protected = true on:

  • The Nexus Capital sector (sector 2551).
  • All sectors in the Gateway Plaza cluster (sectors 2551โ€“2800 per ../../SYSTEMS/central-nexus-clusters.md).
  • A future operator-tunable list (likely Commerce Central Hub, Diplomatic Quarter, and other high-traffic Nexus clusters โ€” to be tuned post-launch).

The same flag is read by POST /api/warp-gates/deploy-beacon in the Phase 1 validation path; deployment is rejected with ERR_NEXUS_PROTECTED_SECTOR before the Sentinel intercept fires.

Faction registration

The Galactic Concord is added as a 9th NPC faction (operator-managed, no player rep gain mechanics) โ€” the Faction row itself is โœ… seeded (create_default_factions + _ensure_concord_faction). Faction.faction_type = CONCORD enum value is โœ… shipped. Emergent/rep maps use code "galactic_concord". See Nexus Sentinel Corps above for the โˆ’200-reputation standing penalty a Sentinel kill applies against this faction.

The Federation Police are not a separate faction โ€” they're the enforcement arm of the existing Terran Federation. Their patrol entries carry faction_code = "terran_federation"; their squad_kind = "federation_marshal" distinguishes them from generic Federation patrols.

NPC-only hull classes

ShipType enum extended with two NPC-only values:

  • NPC_MARSHAL_INTERDICTOR (โœ… Shipped).
  • NPC_SENTINEL_INTERDICTOR (โœ… Shipped).

Both are filtered from any player-facing API that lists ship types (GET /api/v1/ships/types, the construction UI, the TradeDock catalog). The filter lives at the serializer layer; the model itself can hold them via the standard Ship table.

ShipSpecification.is_npc_only = true for both hull classes (โœ… Shipped column). When set, the model layer rejects any registry-event that would transfer the ship to a player (ERR_NPC_ONLY_HULL).

Player interactions

Common-case rules of engagement

A compliant player should never see either force engage them. The mental model the docs should give players:

  • "Stay out of trouble in Federation space โ€” Federation Marshals don't bother lawful pilots."
  • "Don't try to plant a warp gate at the Nexus Capital โ€” the Sentinels will intercept and there's no negotiation."
  • "If you're Wanted, the Federation Zone is dangerous; the Nexus is partially safer (Sentinels don't enforce personal rep) but still off-limits to anything that touches their hub-invariant triggers."

Nexus as a partial sanctuary for Wanted players

The deliberate design choice โ€” Sentinels don't enforce personal reputation โ€” gives Wanted players a transit route through the Nexus that Federation Police can't follow into. This is by design: the Nexus is the universal trade hub, and locking Wanted players out of it would degrade the hub's economic role. The price is that Wanted players cannot interact with the Nexus's protected-sector functions (warp-gate construction, Capital combat) โ€” they can transit, trade, refuel, and leave.

A Wanted player attempting to transit the Federation Zone of a player region to reach the Nexus warp gate will get intercepted by Federation Police at some point during that transit; reaching the Nexus warp gate is itself the challenge. This is the intended pressure.

Bounty interaction

Players cannot place bounties on Interdictor hulls (the bounty-placement endpoint rejects with ERR_BOUNTY_NPC_ONLY_TARGET per ./bounties.md โ€” ๐Ÿ“ Design-only validation). Players who destroy a Federation Marshal Interdictor or Nexus Sentinel Interdictor receive no bounty payout (the system-bounty path doesn't fire on NPC-only kills).

Generator integration

The galaxy generator's Phase 12.5 (per ../../SYSTEMS/galaxy-generator-design.md) hands off to the NPC scheduler rather than stamping anonymous patrol squads:

  • Phase 12.5a โ€” Region rosters (bang-stamped per ADR-0069). For each non-Nexus region, NPCRoster rows with the target counts from the table above are emitted by sw2102-bang in the Universe payload and persisted by the gameserver on import (see ../../SYSTEMS/bang-import-pipeline.md#135-import-npc-rosters). The scheduler's Loop B (roster maintenance) handles the actual NPC creation at runtime โ€” bang and the gameserver do not create individual NPCCharacter rows.
  • Phase 12.5b โ€” Nexus rosters (bang-stamped per ADR-0069). NPCRoster rows for the Sentinel Corps are emitted by sw2102-bang and persisted by the gameserver on import. The gameserver sets Sector.is_nexus_protected = true on the Capital sector and Gateway Plaza cluster sectors during the import.
  • Phase 12.5c โ€” Initial NPC bootstrap (post-import hook, gameserver-side). After the import transaction commits, the gameserver fires a one-time npc_scheduler.bootstrap_region(region_id) call that spawns the initial roster of named NPCs from the name_pool of each NPCRoster. This step happens outside the worldgen transaction so a roster-spawn failure doesn't block region creation; if it fails, Loop B picks up the slack on its next 10-minute tick.

Putting NPC creation behind the scheduler (rather than baking it into worldgen) means an operator can rebalance roster sizes, change name pools, or promote a Marshal to Captain without regenerating a region. The data model rule is clear: NPCRoster is worldgen-stamped; NPCCharacter is runtime-managed.

Player-facing affordances

Verified vs Sectorwars2102 origin/feat 46bce720.

  • โœ… Interdiction combat banner โ€” NpcCombatBanner.tsx (mounted GameLayout.tsx) on npc_combat_initiated frames from WebSocketContext.tsx:951. Tests pin Marshal interdict copy (NpcCombatBanner.test.tsx).
  • โœ… Annunciator LAW contacts โ€” useAnnunciatorState.ts:281 lights LAW when any in-sector contact is isLawArchetype (LAW_ENFORCEMENT / FACTION_PATROL / STATION_SECURITY).
  • โœ… Windshield marshal presence โ€” law hulls visible in flight tableau; shipFaction() colors LAW_ENFORCEMENT blue (SolarSystemViewscreen.tsx:342-343, imported by WindshieldTableau.tsx).
  • ๐Ÿšง En-route countdown banner โ€” tip-absent. GS PendingEngagement + npc_engagement_service.route_engagement + engagement_summary() shipped; player-client git grep PendingEngagement / en route / turns to arrival empty on tip. Do not read GS payload copy as live player HUD.
  • ๐Ÿ“ Named-officer briefing UI โ€” intro examples (Marshal Vance / Sentinel-Captain Lyra Kett shift copy) are canon illustration, not a product requirement. No player-live named-officer dossier on tip.

Source map

Concern Target path
Interdict banner services/player-client/src/components/combat/NpcCombatBanner.tsx
WS frame services/player-client/src/contexts/WebSocketContext.tsx (npc_combat_initiated)
LAW lamp services/player-client/src/components/hud/useAnnunciatorState.ts
Law hull color services/player-client/src/components/tactical/SolarSystemViewscreen.tsx shipFaction()
Flight presence services/player-client/src/components/tactical/WindshieldTableau.tsx
En-route countdown (PC gap) (tip-absent โ€” no player-client consumer on tip)

Status

๐Ÿšง Partial (re-verified 2026-08-06). Both police forces, the two Interdictor hull classes (Federation Marshal Interdictor and Nexus Sentinel Interdictor), the Sector.is_nexus_protected flag, the Faction.faction_type = CONCORD enum value, the ShipSpecification.is_npc_only boolean, the Interdictor Field / Beacon Disruptor / Contraband Scanner abilities, and the Phase 12.5 generator extension are at mixed maturity.

โœ… Enforced in code: Interdictor hull classes + is_npc_only purchase reject; is_nexus_protected column + Phase 1 deploy-beacon rejection (warp_gate_service); named-NPC rosters + scheduler engagement routing (npc_spawn_service / npc_engagement_service); Sector.defenses.police_patrol_ships integration; Marshal-kill Federation rep hook; FactionType.CONCORD enum + Galactic Concord Faction row seed (create_default_factions / _ensure_concord_faction); Sentinel-kill Concord hook at โˆ’200 (police-forces.md).

๐Ÿšง Partial / incomplete: Phase 12.5a/b/c roster+bootstrap paths are wired. Remaining Nexus gaps: Interdictor abilities (inert labels); optional is_npc_barracks_sector lodging. (is_nexus_protected Capital+Gateway Plaza stamp: bang-import predicate shipped โ€” apply on next full Nexus import / regen.) Player-client "police en route" arrival countdown is tip-absent on 46bce720 (empty player-client grep under services/player-client/src).

๐Ÿ“ Design-only / unbuilt: Interdictor Field / Beacon Disruptor / Contraband Scanner / Concord Authorization (JSONB labels only, inert); optional is_npc_barracks_sector lodging.

Cross-references