diff --git a/Content.Server/_CorvaxNext/Storage/AnchorableStorageComponent.cs b/Content.Server/_CorvaxNext/Storage/AnchorableStorageComponent.cs
new file mode 100644
index 00000000000..cf1a10fdc60
--- /dev/null
+++ b/Content.Server/_CorvaxNext/Storage/AnchorableStorageComponent.cs
@@ -0,0 +1,8 @@
+namespace Content.Server._CorvaxNext.Storage;
+
+///
+/// This is used for restricting anchor operations on storage (one bag max per tile)
+/// and ejecting sapient contents on anchor.
+///
+[RegisterComponent]
+public sealed partial class AnchorableStorageComponent : Component;
\ No newline at end of file
diff --git a/Content.Server/_CorvaxNext/Storage/AnchorableStorageSystem.cs b/Content.Server/_CorvaxNext/Storage/AnchorableStorageSystem.cs
new file mode 100644
index 00000000000..b655f6c6124
--- /dev/null
+++ b/Content.Server/_CorvaxNext/Storage/AnchorableStorageSystem.cs
@@ -0,0 +1,99 @@
+using System.Linq;
+using Content.Server.Popups;
+using Content.Shared.Construction.Components;
+using Content.Shared.Mind.Components;
+using Content.Shared.Storage;
+using Robust.Server.GameObjects;
+using Robust.Shared.Containers;
+using Robust.Shared.Map.Components;
+
+namespace Content.Server._CorvaxNext.Storage;
+
+///
+/// This is used for restricting anchor operations on storage (one bag max per tile)
+/// and ejecting living contents on anchor.
+///
+public sealed class AnchorableStorageSystem : EntitySystem
+{
+ [Dependency] private readonly MapSystem _map = default!;
+ [Dependency] private readonly PopupSystem _popup = default!;
+ [Dependency] private readonly TransformSystem _xform = default!;
+ [Dependency] private readonly SharedContainerSystem _container = default!;
+
+ ///
+ public override void Initialize()
+ {
+ SubscribeLocalEvent(OnAnchorStateChanged);
+ SubscribeLocalEvent(OnAnchorAttempt);
+ SubscribeLocalEvent(OnInsertAttempt);
+ }
+
+ private void OnAnchorStateChanged(Entity ent, ref AnchorStateChangedEvent args)
+ {
+ if (!args.Anchored)
+ return;
+
+ var transform = Transform(ent);
+
+ if (CheckOverlap(ent, transform))
+ {
+ _popup.PopupEntity(Loc.GetString("anchored-storage-already-present"), ent);
+ _xform.Unanchor(ent, transform);
+ return;
+ }
+
+ if (!TryComp(ent.Owner, out var storage))
+ return;
+
+ foreach (var item in storage.StoredItems.Keys.ToArray())
+ if (HasComp(item))
+ _container.RemoveEntity(ent, item);
+ }
+
+ private void OnAnchorAttempt(Entity ent, ref AnchorAttemptEvent args)
+ {
+ if (args.Cancelled)
+ return;
+
+ if (!CheckOverlap(ent))
+ return;
+
+ _popup.PopupEntity(Loc.GetString("anchored-storage-already-present"), ent, args.User);
+ args.Cancel();
+ }
+
+ private void OnInsertAttempt(Entity ent, ref ContainerIsInsertingAttemptEvent args)
+ {
+ if (args.Cancelled)
+ return;
+
+ if (!HasComp(args.EntityUid))
+ return;
+
+ if (Transform(ent).Anchored)
+ args.Cancel();
+ }
+
+ public bool CheckOverlap(EntityUid entity, TransformComponent? transform = null)
+ {
+ if (!Resolve(entity, ref transform))
+ return false;
+
+ if (transform.GridUid is not { } grid || !TryComp(grid, out var gridComp))
+ return false;
+
+ var indices = _map.TileIndicesFor(grid, gridComp, transform.Coordinates);
+ var enumerator = _map.GetAnchoredEntitiesEnumerator(grid, gridComp, indices);
+
+ while (enumerator.MoveNext(out var otherEnt))
+ {
+ if (otherEnt == entity)
+ continue;
+
+ if (HasComp(otherEnt))
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/Resources/Locale/en-US/_corvaxnext/storage/anchorable-storage.ftl b/Resources/Locale/en-US/_corvaxnext/storage/anchorable-storage.ftl
new file mode 100644
index 00000000000..1eea4cc9a2e
--- /dev/null
+++ b/Resources/Locale/en-US/_corvaxnext/storage/anchorable-storage.ftl
@@ -0,0 +1 @@
+anchored-storage-already-present = There's already a bag anchored here!
\ No newline at end of file
diff --git a/Resources/Locale/ru-RU/_corvaxnext/storage/anchorable-storage.ftl b/Resources/Locale/ru-RU/_corvaxnext/storage/anchorable-storage.ftl
new file mode 100644
index 00000000000..5f089cb438f
--- /dev/null
+++ b/Resources/Locale/ru-RU/_corvaxnext/storage/anchorable-storage.ftl
@@ -0,0 +1 @@
+anchored-storage-already-present = Здесь уже что-то спрятано!
\ No newline at end of file
diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/detdrobe.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/detdrobe.yml
index ee7d9904269..764a21f6e07 100644
--- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/detdrobe.yml
+++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/detdrobe.yml
@@ -16,3 +16,4 @@
ClothingHandsGlovesColorBlack: 2
ClothingHandsGlovesLatex: 2
ClothingHeadsetSecurity: 2
+ trayScanner: 2 # Corvax-Next-Stashes
diff --git a/Resources/Prototypes/Entities/Clothing/Back/backpacks.yml b/Resources/Prototypes/Entities/Clothing/Back/backpacks.yml
index b589b2bbc08..90fdf3fdf27 100644
--- a/Resources/Prototypes/Entities/Clothing/Back/backpacks.yml
+++ b/Resources/Prototypes/Entities/Clothing/Back/backpacks.yml
@@ -30,6 +30,16 @@
delay: 0.5
- type: ExplosionResistance
damageCoefficient: 0.9
+ # Corvax-Next-Stashes-Start
+ - type: Appearance
+ - type: SubFloorHide
+ - type: Anchorable
+ - type: CollideOnAnchor
+ enable: false
+ - type: Transform
+ anchored: false
+ - type: AnchorableStorage
+ # Corvax-Next-Stashes-End
- type: entity
parent: ClothingBackpack