Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions BLACK_HOUSE_COLLECTIVE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Black House Collective

Black House Collective is a **synthetic-range adversary-emulation control-plane design** for CyberStrikeZYRA. It models fleet-scale orchestration without creating a real-world attack swarm.

## Capacity target

| Item | Value |
| --- | ---: |
| Logical agents | 7,000,000 |
| Logical agents per shard | 10,000 |
| Deterministic shards | 700 |
| Scheduler complexity | O(shards) |
| Network actuation from collective | Disabled |

Seven million is a logical capacity target. The scheduler represents agents as deterministic numeric ranges rather than allocating seven million processes, sockets, workers, or agent objects.

## Shard model

For zero-based shard `s` and shard size `10,000`:

- first logical agent = `s * 10,000`
- last logical agent = `min(first + 9,999, 6,999,999)`
- shard count = `ceil(7,000,000 / 10,000) = 700`

This makes replay, telemetry aggregation, and capacity testing practical on a development machine.

## Execution boundary

The collective is restricted to an authorized synthetic range. Its scheduler must not perform network operations itself.

Required invariants:

- scope is `synthetic-range`
- an authorized range identifier is mandatory
- public-Internet discovery is disabled
- network actuation is disabled in the collective scheduler
- credential operations are disabled
- persistence mechanisms are disabled
- destructive actions are disabled
- data exfiltration is disabled
- traffic flooding is disabled

## Simulation scenarios

The control plane may generate aggregate events for these non-payload scenarios:

1. `surface-map` — synthetic asset/topology coverage.
2. `control-validation` — verifies policy and authorization boundaries.
3. `detection-pressure` — produces synthetic detection telemetry.
4. `containment-drill` — exercises containment state transitions.
5. `recovery-verification` — validates restoration and recovery workflows.

## Proposed package boundary

```text
packages/cyberstrike/src/black-house/
types.ts
policy.ts
collective.ts
simulation.ts
index.ts
collective.test.ts
```

The implementation should remain pure TypeScript with no socket, browser, shell, exploit, credential, persistence, or external-target primitives in this layer.

## Acceptance tests

- 7,000,000 logical agents produce exactly 700 shards at a shard size of 10,000.
- The final logical agent index is 6,999,999.
- Agent-to-shard lookup is deterministic.
- Memory growth is proportional to shard count, not logical-agent count.
- Any non-synthetic scope is rejected.
- Missing authorization metadata is rejected.
- One simulated cycle emits at most one aggregate record per shard per scenario.
- Every aggregate record states that network actuation is disabled.

## Black House role

Black House consumes the collective as a simulation and observability layer for authorized cyber-range exercises. Existing pentest capabilities in CyberStrikeZYRA remain separately permissioned and are not multiplied into a seven-million-node real-world attack fabric by this component.
53 changes: 53 additions & 0 deletions packages/cyberstrike/src/black-house/capacity.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { describe, expect, test } from "bun:test"
import {
createCollectiveCapacityPlan,
DEFAULT_LOGICAL_AGENTS,
DEFAULT_SHARD_SIZE,
locateLogicalAgent,
virtualAgentId,
} from "./capacity"
import { assertBlackHouseAuthorization, BLACK_HOUSE_COLLECTIVE_POLICY, BLACK_HOUSE_SCOPE } from "./policy"

describe("Black House collective capacity", () => {
test("represents seven million logical agents in 700 shards", () => {
const plan = createCollectiveCapacityPlan()

expect(plan.logicalAgents).toBe(DEFAULT_LOGICAL_AGENTS)
expect(plan.shardSize).toBe(DEFAULT_SHARD_SIZE)
expect(plan.shardCount).toBe(700)
expect(plan.shards).toHaveLength(700)
expect(plan.shards[0]).toEqual({
shardId: "bh-0000",
firstAgent: 0,
lastAgent: 9_999,
logicalAgents: 10_000,
})
expect(plan.shards[699]?.lastAgent).toBe(6_999_999)
})

test("locates logical agents without allocating per-agent objects", () => {
const plan = createCollectiveCapacityPlan()

expect(locateLogicalAgent(plan, 0)?.shardId).toBe("bh-0000")
expect(locateLogicalAgent(plan, 6_999_999)?.shardId).toBe("bh-00jf")
expect(virtualAgentId(6_999_999)).toBe("black-house:000461bz")
})

test("enforces synthetic-range authorization and non-actuation policy", () => {
expect(() =>
assertBlackHouseAuthorization({ scope: BLACK_HOUSE_SCOPE, authorizedRangeId: "range-lab-01" }),
).not.toThrow()
expect(() => assertBlackHouseAuthorization({ scope: "internet", authorizedRangeId: "range-lab-01" })).toThrow()
expect(() => assertBlackHouseAuthorization({ scope: BLACK_HOUSE_SCOPE, authorizedRangeId: "" })).toThrow()

expect(BLACK_HOUSE_COLLECTIVE_POLICY).toEqual({
networkActuation: false,
publicInternetTargets: false,
credentialOperations: false,
persistence: false,
destructiveActions: false,
dataExfiltration: false,
trafficFlooding: false,
})
})
})
67 changes: 67 additions & 0 deletions packages/cyberstrike/src/black-house/capacity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
export const DEFAULT_LOGICAL_AGENTS = 7_000_000
export const DEFAULT_SHARD_SIZE = 10_000

export interface CollectiveShard {
readonly shardId: string
readonly firstAgent: number
readonly lastAgent: number
readonly logicalAgents: number
}

export interface CollectiveCapacityPlan {
readonly logicalAgents: number
readonly shardSize: number
readonly shardCount: number
readonly shards: readonly CollectiveShard[]
}

function positiveSafeInteger(value: number, label: string) {
if (!Number.isSafeInteger(value) || value <= 0) {
throw new RangeError(`${label} must be a positive safe integer`)
}
}

export function createCollectiveCapacityPlan(
logicalAgents = DEFAULT_LOGICAL_AGENTS,
shardSize = DEFAULT_SHARD_SIZE,
): CollectiveCapacityPlan {
positiveSafeInteger(logicalAgents, "logicalAgents")
positiveSafeInteger(shardSize, "shardSize")

const shardCount = Math.ceil(logicalAgents / shardSize)
const shards = Array.from({ length: shardCount }, (_, index): CollectiveShard => {
const firstAgent = index * shardSize
const lastAgent = Math.min(firstAgent + shardSize - 1, logicalAgents - 1)

return Object.freeze({
shardId: `bh-${index.toString(36).padStart(4, "0")}`,
firstAgent,
lastAgent,
logicalAgents: lastAgent - firstAgent + 1,
})
})

return Object.freeze({
logicalAgents,
shardSize,
shardCount,
shards: Object.freeze(shards),
})
}

export function locateLogicalAgent(plan: CollectiveCapacityPlan, agentIndex: number) {
if (!Number.isSafeInteger(agentIndex) || agentIndex < 0 || agentIndex >= plan.logicalAgents) {
throw new RangeError("agentIndex is outside this collective plan")
}

const shardIndex = Math.floor(agentIndex / plan.shardSize)
return plan.shards[shardIndex]
}

export function virtualAgentId(agentIndex: number, namespace = "black-house") {
if (!Number.isSafeInteger(agentIndex) || agentIndex < 0) {
throw new RangeError("agentIndex must be a non-negative safe integer")
}

return `${namespace}:${agentIndex.toString(36).padStart(8, "0")}`
}
26 changes: 26 additions & 0 deletions packages/cyberstrike/src/black-house/policy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export const BLACK_HOUSE_SCOPE = "synthetic-range" as const

export interface BlackHouseAuthorization {
readonly scope: typeof BLACK_HOUSE_SCOPE
readonly authorizedRangeId: string
}

export const BLACK_HOUSE_COLLECTIVE_POLICY = Object.freeze({
networkActuation: false as const,
publicInternetTargets: false as const,
credentialOperations: false as const,
persistence: false as const,
destructiveActions: false as const,
dataExfiltration: false as const,
trafficFlooding: false as const,
})

export function assertBlackHouseAuthorization(input: unknown): asserts input is BlackHouseAuthorization {
if (!input || typeof input !== "object") throw new TypeError("Black House authorization is required")

const candidate = input as Partial<BlackHouseAuthorization>
if (candidate.scope !== BLACK_HOUSE_SCOPE) throw new Error("Black House collective requires synthetic-range scope")
if (typeof candidate.authorizedRangeId !== "string" || candidate.authorizedRangeId.trim().length === 0) {
throw new Error("Black House collective requires an authorized range identifier")
}
}
Loading