diff --git a/Content.Client/_CorvaxNext/CartridgeLoader/Cartridges/SecWatchEntryControl.xaml b/Content.Client/_CorvaxNext/CartridgeLoader/Cartridges/SecWatchEntryControl.xaml
new file mode 100644
index 00000000000..12440db5d9b
--- /dev/null
+++ b/Content.Client/_CorvaxNext/CartridgeLoader/Cartridges/SecWatchEntryControl.xaml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Content.Client/_CorvaxNext/CartridgeLoader/Cartridges/SecWatchEntryControl.xaml.cs b/Content.Client/_CorvaxNext/CartridgeLoader/Cartridges/SecWatchEntryControl.xaml.cs
new file mode 100644
index 00000000000..0b3bd362d2c
--- /dev/null
+++ b/Content.Client/_CorvaxNext/CartridgeLoader/Cartridges/SecWatchEntryControl.xaml.cs
@@ -0,0 +1,21 @@
+using Content.Shared.CartridgeLoader.Cartridges;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+
+namespace Content.Client._CorvaxNext.CartridgeLoader.Cartridges;
+
+[GenerateTypedNameReferences]
+public sealed partial class SecWatchEntryControl : BoxContainer
+{
+ public SecWatchEntryControl(SecWatchEntry entry)
+ {
+ RobustXamlLoader.Load(this);
+
+ Status.Text = Loc.GetString($"criminal-records-status-{entry.Status.ToString().ToLower()}");
+ Title.Text = Loc.GetString("sec-watch-entry", ("name", entry.Name), ("job", entry.Job));
+
+ Reason.Text = entry.Reason ?? Loc.GetString("sec-watch-no-reason");
+ }
+}
\ No newline at end of file
diff --git a/Content.Client/_CorvaxNext/CartridgeLoader/Cartridges/SecWatchUi.cs b/Content.Client/_CorvaxNext/CartridgeLoader/Cartridges/SecWatchUi.cs
new file mode 100644
index 00000000000..838f4395c73
--- /dev/null
+++ b/Content.Client/_CorvaxNext/CartridgeLoader/Cartridges/SecWatchUi.cs
@@ -0,0 +1,27 @@
+using Content.Client.UserInterface.Fragments;
+using Content.Shared.CartridgeLoader;
+using Content.Shared.CartridgeLoader.Cartridges;
+using Robust.Client.UserInterface;
+
+namespace Content.Client._CorvaxNext.CartridgeLoader.Cartridges;
+
+public sealed partial class SecWatchUi : UIFragment
+{
+ private SecWatchUiFragment? _fragment;
+
+ public override Control GetUIFragmentRoot()
+ {
+ return _fragment!;
+ }
+
+ public override void Setup(BoundUserInterface ui, EntityUid? owner)
+ {
+ _fragment = new SecWatchUiFragment();
+ }
+
+ public override void UpdateState(BoundUserInterfaceState state)
+ {
+ if (state is SecWatchUiState cast)
+ _fragment?.UpdateState(cast);
+ }
+}
\ No newline at end of file
diff --git a/Content.Client/_CorvaxNext/CartridgeLoader/Cartridges/SecWatchUiFragment.xaml b/Content.Client/_CorvaxNext/CartridgeLoader/Cartridges/SecWatchUiFragment.xaml
new file mode 100644
index 00000000000..4ebea90f59c
--- /dev/null
+++ b/Content.Client/_CorvaxNext/CartridgeLoader/Cartridges/SecWatchUiFragment.xaml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Content.Client/_CorvaxNext/CartridgeLoader/Cartridges/SecWatchUiFragment.xaml.cs b/Content.Client/_CorvaxNext/CartridgeLoader/Cartridges/SecWatchUiFragment.xaml.cs
new file mode 100644
index 00000000000..068ff2c8a38
--- /dev/null
+++ b/Content.Client/_CorvaxNext/CartridgeLoader/Cartridges/SecWatchUiFragment.xaml.cs
@@ -0,0 +1,25 @@
+using Content.Shared.CartridgeLoader.Cartridges;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+
+namespace Content.Client._CorvaxNext.CartridgeLoader.Cartridges;
+
+[GenerateTypedNameReferences]
+public sealed partial class SecWatchUiFragment : BoxContainer
+{
+ public SecWatchUiFragment()
+ {
+ RobustXamlLoader.Load(this);
+ }
+
+ public void UpdateState(SecWatchUiState state)
+ {
+ NoEntries.Visible = state.Entries.Count == 0;
+ Entries.RemoveAllChildren();
+ foreach (var entry in state.Entries)
+ {
+ Entries.AddChild(new SecWatchEntryControl(entry));
+ }
+ }
+}
\ No newline at end of file
diff --git a/Content.Server/_CorvaxNext/CartridgeLoader/Cartridges/SecWatchCartridgeComponent.cs b/Content.Server/_CorvaxNext/CartridgeLoader/Cartridges/SecWatchCartridgeComponent.cs
new file mode 100644
index 00000000000..c0ca5ad9dee
--- /dev/null
+++ b/Content.Server/_CorvaxNext/CartridgeLoader/Cartridges/SecWatchCartridgeComponent.cs
@@ -0,0 +1,23 @@
+using Content.Shared.Security;
+
+namespace Content.Server.CartridgeLoader.Cartridges;
+
+[RegisterComponent, Access(typeof(SecWatchCartridgeSystem))]
+public sealed partial class SecWatchCartridgeComponent : Component
+{
+ ///
+ /// Only show people with these statuses.
+ ///
+ [DataField]
+ public List Statuses = new()
+ {
+ SecurityStatus.Wanted,
+ SecurityStatus.Detained
+ };
+
+ ///
+ /// Station entity thats getting its records checked.
+ ///
+ [DataField]
+ public EntityUid? Station;
+}
diff --git a/Content.Server/_CorvaxNext/CartridgeLoader/Cartridges/SecWatchCartridgeSystem.cs b/Content.Server/_CorvaxNext/CartridgeLoader/Cartridges/SecWatchCartridgeSystem.cs
new file mode 100644
index 00000000000..2b46637da12
--- /dev/null
+++ b/Content.Server/_CorvaxNext/CartridgeLoader/Cartridges/SecWatchCartridgeSystem.cs
@@ -0,0 +1,73 @@
+using Content.Server.Station.Systems;
+using Content.Server.StationRecords;
+using Content.Server.StationRecords.Systems;
+using Content.Shared.CartridgeLoader;
+using Content.Shared.CartridgeLoader.Cartridges;
+using Content.Shared.CriminalRecords;
+using Content.Shared.StationRecords;
+
+namespace Content.Server.CartridgeLoader.Cartridges;
+
+public sealed class SecWatchCartridgeSystem : EntitySystem
+{
+ [Dependency] private readonly CartridgeLoaderSystem _cartridgeLoader = default!;
+ [Dependency] private readonly StationRecordsSystem _records = default!;
+ [Dependency] private readonly StationSystem _station = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnRecordModified);
+
+ SubscribeLocalEvent(OnUiReady);
+ }
+
+ private void OnRecordModified(RecordModifiedEvent args)
+ {
+ // when a record is modified update the ui of every loaded cartridge tuned to the same station
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var uid, out var comp, out var cartridge))
+ {
+ if (cartridge.LoaderUid is not {} loader || comp.Station != args.Station)
+ continue;
+
+ UpdateUI((uid, comp), loader);
+ }
+ }
+
+ private void OnUiReady(Entity ent, ref CartridgeUiReadyEvent args)
+ {
+ UpdateUI(ent, args.Loader);
+ }
+
+ private void UpdateUI(Entity ent, EntityUid loader)
+ {
+ // if the loader is on a grid, update the station
+ // if it is off grid use the cached station
+ if (_station.GetOwningStation(loader) is {} station)
+ ent.Comp.Station = station;
+
+ if (!TryComp(ent.Comp.Station, out var records))
+ return;
+
+ station = ent.Comp.Station.Value;
+
+ var entries = new List();
+ foreach (var (id, criminal) in _records.GetRecordsOfType(station, records))
+ {
+ if (!ent.Comp.Statuses.Contains(criminal.Status))
+ continue;
+
+ var key = new StationRecordKey(id, station);
+ if (!_records.TryGetRecord(key, out var general, records))
+ continue;
+
+ var status = criminal.Status;
+ entries.Add(new SecWatchEntry(general.Name, general.JobTitle, criminal.Status, criminal.Reason));
+ }
+
+ var state = new SecWatchUiState(entries);
+ _cartridgeLoader.UpdateCartridgeUiState(loader, state);
+ }
+}
\ No newline at end of file
diff --git a/Content.Shared/_CorvaxNext/CartridgeLoader/Cartridges/SecWatchUiState.cs b/Content.Shared/_CorvaxNext/CartridgeLoader/Cartridges/SecWatchUiState.cs
new file mode 100644
index 00000000000..29720630c43
--- /dev/null
+++ b/Content.Shared/_CorvaxNext/CartridgeLoader/Cartridges/SecWatchUiState.cs
@@ -0,0 +1,24 @@
+using Content.Shared.Security;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.CartridgeLoader.Cartridges;
+
+///
+/// Show a list of wanted and suspected people from criminal records.
+///
+[Serializable, NetSerializable]
+public sealed class SecWatchUiState : BoundUserInterfaceState
+{
+ public readonly List Entries;
+
+ public SecWatchUiState(List entries)
+ {
+ Entries = entries;
+ }
+}
+
+///
+/// Entry for a person who is wanted or suspected.
+///
+[Serializable, NetSerializable]
+public record struct SecWatchEntry(string Name, string Job, SecurityStatus Status, string? Reason);
\ No newline at end of file
diff --git a/Resources/Locale/en-US/_corvaxnext/cartridge-loader/secwatch.ftl b/Resources/Locale/en-US/_corvaxnext/cartridge-loader/secwatch.ftl
new file mode 100644
index 00000000000..ca18db233aa
--- /dev/null
+++ b/Resources/Locale/en-US/_corvaxnext/cartridge-loader/secwatch.ftl
@@ -0,0 +1,5 @@
+sec-watch-program-name = SecWatch
+sec-watch-title = SecWatch 1.0
+sec-watch-no-entries = Everything's calm. Why not enjoy a Monkin Donut?
+sec-watch-entry = {$name}
+sec-watch-no-reason = None given???
\ No newline at end of file
diff --git a/Resources/Locale/ru-RU/_corvaxnext/cartridge-loader/secwatch.ftl b/Resources/Locale/ru-RU/_corvaxnext/cartridge-loader/secwatch.ftl
new file mode 100644
index 00000000000..bd9a5070cb0
--- /dev/null
+++ b/Resources/Locale/ru-RU/_corvaxnext/cartridge-loader/secwatch.ftl
@@ -0,0 +1,5 @@
+sec-watch-program-name = ОКО СБ
+sec-watch-title = ОКО СБ 1.0
+sec-watch-no-entries = Полный порядок. Безопасность превыше всего.
+sec-watch-entry = {$name}
+sec-watch-no-reason = Причина не уточнена.
\ No newline at end of file
diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/_corvaxnext/entities/objects/devices/cartriges.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/_corvaxnext/entities/objects/devices/cartriges.ftl
new file mode 100644
index 00000000000..427dadde5e1
--- /dev/null
+++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/_corvaxnext/entities/objects/devices/cartriges.ftl
@@ -0,0 +1,2 @@
+ent-SecWatchCartridge = картридж "око сб"
+ .desc = Картридж, отслеживающий статус разыскиваемых службой безопасности лиц.
\ No newline at end of file
diff --git a/Resources/Prototypes/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Entities/Objects/Devices/pda.yml
index 4febc87b90f..8b13d2598bb 100644
--- a/Resources/Prototypes/Entities/Objects/Devices/pda.yml
+++ b/Resources/Prototypes/Entities/Objects/Devices/pda.yml
@@ -76,6 +76,7 @@
- CrewManifestCartridge
- NotekeeperCartridge
- NewsReaderCartridge
+ - SecWatchCartridge # Corvax-Next-SecWatch
cartridgeSlot:
priority: -1
name: device-pda-slot-component-slot-name-cartridge
@@ -138,6 +139,7 @@
- NotekeeperCartridge
- NewsReaderCartridge
- MedTekCartridge
+ - SecWatchCartridge # Corvax-Next-SecWatch
- type: entity
parent: BasePDA
@@ -408,6 +410,7 @@
- NotekeeperCartridge
- NewsReaderCartridge
- AstroNavCartridge
+ - SecWatchCartridge # Corvax-Next-SecWatch
- type: entity
parent: BasePDA
@@ -773,6 +776,7 @@
- WantedListCartridge
- MedTekCartridge
- AstroNavCartridge
+ - SecWatchCartridge # Corvax-Next-SecWatch
- type: entity
parent: CentcomPDA
diff --git a/Resources/Prototypes/_CorvaxNext/Entities/Objects/Devices/cartridges.yml b/Resources/Prototypes/_CorvaxNext/Entities/Objects/Devices/cartridges.yml
new file mode 100644
index 00000000000..b271eb6378f
--- /dev/null
+++ b/Resources/Prototypes/_CorvaxNext/Entities/Objects/Devices/cartridges.yml
@@ -0,0 +1,20 @@
+- type: entity
+ parent: BaseItem
+ id: SecWatchCartridge
+ name: sec watch cartridge
+ description: A cartridge that tracks the status of currently wanted individuals.
+ components:
+ - type: Sprite
+ sprite: _CorvaxNext/Objects/Devices/cartridge.rsi
+ state: cart-cri
+ - type: Icon
+ sprite: _CorvaxNext/Objects/Devices/cartridge.rsi
+ state: cart-cri
+ - type: UIFragment
+ ui: !type:SecWatchUi
+ - type: Cartridge
+ programName: sec-watch-program-name
+ icon:
+ sprite: Objects/Weapons/Melee/stunbaton.rsi
+ state: stunbaton_on
+ - type: SecWatchCartridge
\ No newline at end of file
diff --git a/Resources/Textures/_CorvaxNext/Objects/Devices/cartridge.rsi/cart-cri.png b/Resources/Textures/_CorvaxNext/Objects/Devices/cartridge.rsi/cart-cri.png
new file mode 100644
index 00000000000..4da4343e584
Binary files /dev/null and b/Resources/Textures/_CorvaxNext/Objects/Devices/cartridge.rsi/cart-cri.png differ
diff --git a/Resources/Textures/_CorvaxNext/Objects/Devices/cartridge.rsi/meta.json b/Resources/Textures/_CorvaxNext/Objects/Devices/cartridge.rsi/meta.json
new file mode 100644
index 00000000000..0eb27fde855
--- /dev/null
+++ b/Resources/Textures/_CorvaxNext/Objects/Devices/cartridge.rsi/meta.json
@@ -0,0 +1,14 @@
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Timfa, plus edits by portfiend",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "cart-cri"
+ }
+ ]
+}
\ No newline at end of file