ON-CHAIN ASSIGNMENT
One deterministic PDA normally maps one 100 x 100 Chunk cell
A GuardianRegion address derives from `guardian-region`, the global configuration, and signed little-endian Region X and Z coordinates. Its 288-byte V2 record stores inclusive Chunk bounds, treasury owner, separate operator, endpoint host, port, TLS flag, status, update slot, and compact building-manifest metadata. Ordinary coordinates produce the intended 100 x 100 bounds. The program does not range-check Region inputs and uses saturating multiply and add at extreme i32 values, where those stored bounds can collapse. Because coordinates are seeds, two operators cannot create two active PDAs for the same coordinate pair.
The Genesis route requires the registering operator and configured governance wallet to sign and succeeds only while the Registry's Genesis flag is unset. Each normal tag-2 registration supplies the four cardinal neighbor PDA addresses, and at least one must decode as an active Region; a diagonal or arbitrary service rectangle does not satisfy that route. `add_active` preserves the Genesis flag during normal registration, so the one-time Genesis route does not reopen.
Registration transfers 100,000 NCK—100,000,000,000 base units at six decimals—from the operator token account directly into the treasury token account. Despite the source constant's stake name, the current program does not hold a per-operator escrow and exposes no withdraw instruction. GuardianRegion.owner remains the treasury wallet, while GuardianRegion.operator identifies the wallet allowed to update the endpoint; governance can rotate that operator through a separate instruction.
The PDA, not an arbitrary rectangle claim, identifies the assigned cell.
The account stores min = 100r and max = 100r + 99 while both results fit signed i32.
It is not withdrawable escrow in the current Guardian program.
REGISTRY AUDIT
Registry counters summarize registrations; Region accounts provide the discovery map
The 160-byte GuardianRegistry layout places activeCount at byte 108, totalRegistrations at 116, the Genesis flag at 124, Region size at 126, the recorded registration transfer at 128, slash placeholder at 136, creation slot at 144, and creation time at 152. Current `add_active` reads and writes activeCount at 108 and totalRegistrations at 116, then sets byte 124 only for the one Genesis registration. These writes do not overlap one another or the following policy fields.
Both counters increment once after a Region is packed successfully. The current instruction set has no Region removal, deactivation, or reactivation path, so activeCount and totalRegistrations advance together under ordinary current-source execution. They remain summaries, not a directory: GuardianRegistry stores no Region coordinates, PDA addresses, operators, or endpoints. Operators and indexers therefore enumerate 288-byte GuardianRegion accounts owned by the Guardian Program, validate their layout, and filter active status to construct the actual map. The website likewise derives endpoint rows and its active figure from that account scan, while the separate total figure reads totalRegistrations.
Current instruction dispatch accepts only tags 0, 1, 2, 5, 6, and 8. Retired proof and settlement tags 3 and 4 are explicitly rejected. Proof timestamps, proof count, penalty count, slashed amount, and similar bytes remain layout placeholders; there is no current proof submission, reward, slash, Region removal, registration refund, or withdrawal path.
Validate program owner, NCKGRG01 magic, V2 layout, treasury owner, and active status.
Both u64 fields increment once per successful registration without overlapping the Genesis flag or policy fields.
Tags 3 and 4 from the retired proof/settlement design return InvalidInstruction.
PLAY CLIENT DISCOVERY
Play derives nearby Region PDAs before opening any relay connection
The current `/play/` resolver maps the player's Chunk to a Region and preloads that cell plus its eight surrounding cells. It derives the exact PDAs locally, requests them with `getMultipleAccounts` at confirmed commitment, and accepts only the configured Guardian Program owner, 288-byte length, NCKGRG01 magic, V2 layout, treasury owner, active status, and nonempty host. The full host, port, and TLS fields become the WebSocket URL and corresponding buildings URL.
Successful and missing entries become eligible for refresh after five minutes; an older active entry can continue serving from cache while refresh runs or after a failed refresh until another result replaces it or the cache is cleared. A first uncached RPC error creates a thirty-second retry entry, while a failed refresh of an existing stale entry preserves its old timestamp and can be attempted again immediately. Changing RPC endpoints clears the cache. If the current Region has no accepted cached account, realtime remains disabled until discovery succeeds. `/play/` does not fall back to a bundled Guardian URL or a localStorage override, and local rendering and mining can continue without the relay.
These checks prove that endpoint metadata came from the expected chain account. They do not dial the endpoint as part of registration validation, prove that its certificate remains valid, measure its capacity, or show that the process is online. The older `src/` browser implementation still contains query, localStorage, and hardcoded fallback behavior; that legacy path must not be used to describe `/play/`.
- Compute the current Region
Floor-divide each signed Chunk coordinate by 100 and derive the center plus eight neighboring Region PDAs.
- Read exact accounts
Fetch those addresses in confirmed batches instead of scanning every Guardian account on each player move.
- Validate provenance and layout
Require program owner, fixed bytes, magic, version, treasury owner, active status, and host before constructing URLs.
- Connect or stay local
Use the accepted cached endpoint; on a miss, keep realtime disabled while ordinary local play and authoritative chain actions remain available.
REALTIME RUNTIME
The node coalesces nearby binary hints inside a configured window
Guardian is a single-process C++ WebSocket relay with one little-endian binary message per frame, no realtime JSON, no per-message compression, and local Chunk topics. The default service center is Chunk 0,0 with radius 100, producing a 201 x 201 runtime window. That square is an operator configuration, not the registered 100 x 100 Region. A node can accept a wider runtime window, but clients outside its assigned Region do not discover that extra reach from this Region PDA unless their own Region metadata points to it.
The default AOI radius is seven, so joins, leaves, movement, DIG events, chat, identity, and equipment are relayed only across a clipped 15 x 15 neighborhood. Latest movement replaces an earlier pending pose for the same player, destination topics receive compact batches, and each batch is capped at 255 records. This limits fanout and allocation pressure without converting the relay into a simulation authority.
HELLO is 52 bytes: protocol fields, 32 claimed wallet bytes, starting Chunk coordinates, and a random nonce. The server verifies message shape, nonzero wallet bytes, capacity, and service-range membership, but never verifies a wallet signature or uses the nonce in a challenge. A client can therefore spoof a wallet hint and retire an existing relay connection that claimed the same bytes. MOVE has no speed, collision, or physical-plausibility check; DIG proximity is a relay filter, not a mining proof.
Independent runtime configuration centered on one Chunk.
AOI fanout is clipped at service boundaries and never broadcasts across the whole service square.
Wallet, name, equipment, chat, pose, and DIG identity are presentation hints.
FINAL STATE BOUNDARY
Fast relay feedback does not become Solana state
During mining, the current Play client sends Guardian action 1 when a local transaction is pending. Only after its own transaction confirms does that client send action 3. A receiver uses action 3 to render the block as air immediately, then invalidates the affected chain Chunk and requests a fresh account sync. This is a latency optimization, not evidence embedded in the packet.
Because HELLO identity and subsequent frames are unsigned, a remote sender can fabricate an action 3 hint. The current receiver protects that injected air delta until a full account snapshot first reports the same value; when the authoritative snapshot disagrees, the protected hint is retained instead of cleared. A forged hint can therefore leave the local block view stale even though it cannot change the on-chain Chunk account, create a mining reward, or alter inventory. The same unsigned boundary applies to avatar identity, movement, chat, names, and visible equipment.
The client backs off failed reconnections from one second by a factor of 1.75, capped at twelve seconds plus bounded jitter. Only sockets that close before WebSocket `open` increase the consecutive-failure counter; four such failures mark realtime offline for sixty seconds, clear remote avatars, and keep local mining and rendering available. Because `open` resets the counter, a server that repeatedly accepts the socket and then rejects HELLO or another protocol step can trigger unbounded reconnect cycles. Losing the relay removes low-latency presence; it does not remove the player's Solana accounts or make Guardian the fallback settlement layer.
- Preview locally
Mining animation and a pending action remain reversible while the transaction is unresolved.
- Relay a pending hint
Nearby clients can animate the swing without treating it as a resource transfer or final block mutation.
- Confirm through the chain path
The originating client sends the confirmed hint only after its own transaction confirmation.
- Request an authoritative snapshot
The receiver invalidates and reloads the Chunk account, but the current protected-hint logic does not clear an air hint when that snapshot disagrees.
VERIFIED BUILDING DISCOVERY
The official watcher and browser paths apply separate verification passes
Any connected relay client can submit BUILDING_ANNOUNCE. For an active add or update, the C++ node checks record layout, flags, nonzero foundation ID, geometry, Region intersection, overlap, and revision or slot ordering before changing its local manifest. A flags-zero removal instead requires an existing foundation ID plus a newer slot or revision and skips the add/update geometry checks. Neither route verifies the sender's BuildSite authority or queries a Building PDA. The raw manifest is therefore an untrusted discovery proposal even when its local digest is internally consistent.
The publisher watcher fetches that manifest, accepts V2 or current V3, validates record ordering and Region intersection, then resolves every BuildSite and active BuildingManifest PDA. It compares geometry, active revision, updated slot, owner, status, and 16-byte building-hash prefix. Only governance or the dedicated blueprint publisher can sign the Building Program's outer instruction. That program validates the publisher and Guardian Program, then signs a CPI with its `guardian-blueprint` PDA. The Guardian Program accepts only that PDA and applies a monotonic hash, revision, and record-count update. These PDA checks are implemented by the watcher, not forced by either on-chain program: an allowlisted publisher could bypass the script and submit arbitrary metadata directly. The watcher also returns `unchanged` when only hash and treasury owner match; it does not compare revision or count there, so identical records under a newer relay revision can leave chain metadata stale.
Current C++ source emits NCKBRG03: first16(SHA-256) over ordered 56-byte records. The sync and browser code also accept the legacy NCKBRG02 non-cryptographic dual-64-bit digest. GuardianRegion stores the resulting 16 bytes, u64 revision, and u32 count but no digest-algorithm version, and the on-chain program does not receive the records or recompute either algorithm. The commitment proves what the authorized path wrote, while the off-chain verifiers determine what those bytes describe.
The browser obtains a fresh manifest only from the chain-discovered Region endpoint, requires its Region, digest, revision, and count to match the current chain-derived resolver entry, rejects duplicate or out-of-Region records, and reloads every changed BuildSite. It repeats the resolver-metadata comparison before and after writing IndexedDB, but does not issue a new RPC read during those checks. `guardianRecordMatchesFoundation` does not compare the record's 16-byte content hash with a BuildingManifest, so this regional pass is BuildSite-checked rather than a complete payload proof. Before rendering, the separate building loader requires the full 32-byte BuildingManifest hash to start with that 16-byte prefix. The fast regional-cache path requires the current `guardian-blueprint-v3` schema and `verified: true`, then compares only the cached digest hash with the resolver hash; it does not also compare cached revision or count.
Records are ordered by foundation ID before the first 16 SHA-256 bytes are retained.
No algorithm-version field or record bytes are stored in GuardianRegion.
Only current-schema, explicitly verified entries are eligible for reuse.
DISCOVERY AND TRUST MODEL
Keep the assigned cell, runtime window, AOI, and final state separate
These equations distinguish the on-chain 100 x 100 Region from a node's independently configured service square, quantify the smaller realtime fanout, and show the commitment checks that turn an untrusted regional manifest into a verified discovery cache.
Map a Chunk to one assigned Region
r(c) = floor(c / 100); minChunk(r) = 100r; maxChunk(r) = 100r + 99The same floor rule works across zero and negative coordinates. Applying it independently to X and Z gives one deterministic GuardianRegion PDA and a fixed 100 x 100 Chunk rectangle while 100r and 100r + 99 fit signed i32; the program does not range-check Region inputs and uses saturating arithmetic at extreme coordinates. An active PDA prevents a second active registration for the same coordinate pair.
- c
- An integer world Chunk coordinate on one horizontal axis.
- r
- The signed Region coordinate on that axis.
- 100r .. 100r + 99
- The inclusive on-chain Chunk bounds stored in the Region account.
Normal registrations require a cardinal neighbor
cardinal(a,b) iff |a_x - b_x| + |a_z - b_z| = 1A tag-2 registration supplies the east, west, north, and south PDA addresses and succeeds when at least one is an active Region. A diagonal neighbor has Manhattan distance two and cannot satisfy that route. The Genesis flag is stored separately and remains set after normal registrations, so the treasury-signed tag-1 route can succeed only once; every later Region must extend the active set through a cardinal neighbor.
- a
- The Region coordinate being registered.
- b
- An already active neighboring Region coordinate.
- |dx| + |dz|
- Manhattan distance on the signed Region grid.
Service reach and realtime AOI are different squares
service(R) = (2R + 1)^2; service(100) = 40,401 Chunks; AOI(r) = (2r + 1)^2; AOI(7) = 225 ChunksThe default C++ node accepts connections in a 201 x 201 configured window, but that setting does not enlarge its 100 x 100 on-chain assignment or make the extra window chain-discoverable. Player, movement, DIG, chat, identity, and equipment broadcasts use a clipped 15 x 15 AOI instead of all 40,401 service topics; building digests use a separate manifest topic.
- R
- The runtime service radius in Chunks around a configured center.
- r
- The runtime area-of-interest radius used for nearby publication.
- 40,401
- The number of Chunk topics in the default runtime window, independent of the registered Region rectangle.
Batch wire cost grows linearly with nearby activity
MOVE_BATCH_down(n) = 4 + 12n bytes; DIG_BATCH_up(n) = 2 + 10n bytes; DIG_EVENT_BATCH_down(n) = 6 + 9n bytes; 1 <= n <= 255Pending movement is coalesced per player, grouped by destination topic, and split into downlink batches of at most 255 items. Clients can submit up to 255 DIG records in one uplink batch, while the relay emits compact DIG-event downlink batches. A configured DIG rate limit counts incoming frames, so one accepted DIG_BATCH can contain many records; it must not be described as a limit of ten individual blocks per second.
- n
- The number of compact records carried in one batch frame.
- 4, 2, or 6
- The fixed header bytes for MOVE downlink, DIG uplink, or DIG-event downlink batches.
- 12, 10, or 9
- The encoded bytes per movement, submitted DIG, or relayed DIG-event record.
A committed building index still needs independent verification
D_V3 = first16(SHA256(R_0 || ... || R_(n-1))); applyFresh = endpointMatch AND manifestMeta = resolverMeta AND changedBuildSitesMatch AND resolverMetaStable; reuseCache = verifiedSchema AND D_cache = D_resolverCurrent V3 Guardian source hashes ordered 56-byte records and keeps the first 16 SHA-256 bytes. The sync and browser paths also accept the legacy non-cryptographic V2 dual-64-bit digest. The Region account stores only 16 digest bytes, revision, and count—no algorithm version—and the Guardian Program does not recompute the manifest. The browser compares fresh manifests with chain-derived resolver metadata and reloads changed BuildSites, but that pass does not compare each record's content-hash prefix with a BuildingManifest; the building loader performs that later. Resolver stability checks do not make a new RPC read, and the verified-cache fast path compares only the digest hash, not revision and count.
- R_i
- The ith canonical 56-byte regional building record, ordered by foundation ID.
- D
- The 16-byte digest committed in GuardianRegion metadata.
- resolverMetaStable
- The current in-memory Guardian resolver entry still matches around verification and cache publication; this is not a fresh chain RPC read.
Play accepts only the expected Region account layout
JavaScriptplay/play-guardian-registry.jsfunction decodeGuardianAccount(item) {
try {
if (String(item?.account?.owner || "") !== NICECHUNK_GUARDIAN_PROGRAM_ID) return null;
const data = item?.account?.data;
const base64 = Array.isArray(data) ? data[0] : data;
const bytes = base64ToBytes(base64);
if (bytes.length !== GUARDIAN_REGION_LEN) return null;
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
const magic = ascii(bytes, 0, 8);
if (magic !== GUARDIAN_REGION_MAGIC) return null;
if (base58Encode(bytes.subarray(36, 68)) !== GUARDIAN_GOVERNANCE_WALLET) return null;
const hostLen = bytes[132] || 0;
const host = ascii(bytes, 133, Math.min(64, hostLen));
const version = view.getUint16(8, true);
if (version !== 2) return null;
The resolver derives exact Region PDAs and rejects the wrong program owner, byte length, magic, governance owner, or version. Its caller also requires active status and a nonempty host. Those checks authenticate account provenance and layout, not whether the advertised network process is currently reachable.
HELLO accepts a claimed nonzero wallet without a signature
C++Guardian/src/app.cpp Hello hello;
if (!decode_hello(message, hello)) {
state.add_log("Rejected HELLO: bad protocol");
close_with_error(ws, ERROR_BAD_PROTOCOL);
return;
}
if (ws->getUserData()->player) {
close_with_error(ws, ERROR_BAD_PROTOCOL);
return;
}
if (wallet_is_empty(hello.wallet_pubkey)) {
state.add_log("Rejected HELLO: missing wallet identity");
close_with_error(ws, ERROR_BAD_PROTOCOL);
return;
}
auto retired = state.retire_players_with_wallet(hello.wallet_pubkey);
for (const auto ¬ice : retired) {
auto leave_payload = encode_player_leave(notice.leave);
state.publish_to_aoi(notice.chunk_x, notice.chunk_z, leave_payload, publish);
state.add_log("Retired duplicate player #" + std::to_string(notice.leave.local_player_id));
}
if (!state.can_accept_player()) {
state.add_log("Rejected HELLO: server full");
close_with_error(ws, ERROR_SERVER_FULL);
return;
}
Player *player = state.create_player(hello, now_ms());
if (!player) {
state.add_log("Rejected HELLO: out of Guardian range");
close_with_error(ws, ERROR_OUT_OF_RANGE);
return;
}
Protocol V1 decodes 32 wallet bytes, a starting Chunk, and an eight-byte client nonce. The handler checks protocol shape, a nonzero wallet, capacity, and configured range, but it neither requests nor verifies an Ed25519 signature or challenge response. The claimed wallet can therefore identify a relay avatar only as a hint.
The publisher compares every regional record with authoritative PDAs
TypeScriptscripts/sync-guardian-blueprint.ts if (site.status !== BUILD_SITE_STATUS_ACTIVE
|| !site.globalConfig.equals(globalConfig)
|| site.foundationId !== record.foundationId
|| site.minX !== record.minX
|| site.minZ !== record.minZ
|| site.surfaceY !== record.surfaceY
|| site.width !== record.width
|| site.depth !== record.depth
|| site.activeRevision !== record.activeRevision
|| record.updatedSlot !== 0n && site.updatedSlot !== record.updatedSlot) {
throw new Error(`Guardian foundation ${record.foundationId} does not match its BuildSite PDA`);
}
if (record.activeRevision > 0) activeRecords.push({ record, owner: site.owner });
}
const buildingPdas = activeRecords.map(({ record }) => deriveBuildingManifestPda({
globalConfig,
foundationId: record.foundationId,
revision: record.activeRevision,
programId: selectedBuildingProgramId,
})[0]);
const buildingAccounts = await getMultipleAccountsInfoBatched(buildingPdas);
for (let index = 0; index < activeRecords.length; index += 1) {
const { record, owner } = activeRecords[index];
const account = buildingAccounts[index];
if (!account || !account.owner.equals(selectedBuildingProgramId)) {
throw new Error(`Guardian building ${record.foundationId}@${record.activeRevision} has no manifest PDA`);
}
verifyActiveBuildingManifest(account.data, record, owner);
}
The watcher does not commit whatever the relay says. It resolves the BuildSite, compares geometry, active revision, and slot, then requires the corresponding active BuildingManifest and owner/hash relationship before an authorized publisher sends the outer Building Program instruction.
Guardian accepts only monotonic, idempotent blueprint metadata
Rustprograms/nicechunk_guardian/src/state.rs let current_revision = u64::from_le_bytes(
data[Self::BLUEPRINT_REVISION_OFFSET..Self::BLUEPRINT_REVISION_OFFSET + 8]
.try_into()
.map_err(|_| NicechunkGuardianError::InvalidGuardianRegionData)?,
);
let current_count = u32::from_le_bytes(
data[Self::BLUEPRINT_RECORD_COUNT_OFFSET..Self::BLUEPRINT_RECORD_COUNT_OFFSET + 4]
.try_into()
.map_err(|_| NicechunkGuardianError::InvalidGuardianRegionData)?,
);
let same_hash = data[Self::BLUEPRINT_HASH_OFFSET..Self::BLUEPRINT_HASH_OFFSET + 16]
== blueprint_hash[..];
if revision < current_revision
|| revision == current_revision && (!same_hash || record_count != current_count)
{
return Err(NicechunkGuardianError::InvalidBlueprintRevision.into());
}
if revision == current_revision {
return Ok(());
}
data[Self::BLUEPRINT_HASH_OFFSET..Self::BLUEPRINT_HASH_OFFSET + 16]
.copy_from_slice(blueprint_hash);
data[Self::BLUEPRINT_REVISION_OFFSET..Self::BLUEPRINT_REVISION_OFFSET + 8]
.copy_from_slice(&revision.to_le_bytes());
data[Self::BLUEPRINT_RECORD_COUNT_OFFSET..Self::BLUEPRINT_RECORD_COUNT_OFFSET + 4]
.copy_from_slice(&record_count.to_le_bytes());
A lower revision is rejected, and the same revision is accepted only when hash and count are unchanged. The instruction path separately requires the `guardian-blueprint` PDA of the configured Building Program; the Building Program accepts only governance or the dedicated publisher and signs the Guardian CPI with that PDA. This writes a commitment, not an on-chain manifest recomputation.
The browser rechecks changed BuildSites around verified cache writes
JavaScriptplay/play-chain-foundations.js async function verifyAndApplyManifest(input, entry) {
const manifest = normalizeManifest(input);
if (manifest.regionX !== entry.region.x || manifest.regionZ !== entry.region.z) {
throw new Error("Guardian building manifest region mismatch.");
}
requireManifestStillCurrent(manifest, entry);
const records = manifest.records.map((record) => normalizeGuardianRecord(record, manifest));
const ids = new Set();
for (const record of records) {
if (ids.has(record.foundationId)) throw new Error("Guardian building manifest contains duplicate foundation IDs.");
ids.add(record.foundationId);
if (!recordIntersectsGuardianRegion(record, manifest.regionX, manifest.regionZ, chunkSize)) {
throw new Error("Guardian building record does not intersect its region.");
}
}
const cached = await cache?.getRegion?.(manifest.regionX, manifest.regionZ);
const previousRecords = new Map((cached?.records ?? []).map((record) => [String(record.foundationId), record]));
const previousFoundations = new Map((cached?.foundations ?? []).map((foundation) => [String(foundation.foundationId), foundation]));
const foundationsById = new Map();
const changedRecords = [];
for (const record of records) {
const previousRecord = previousRecords.get(record.foundationId);
const previousFoundation = previousFoundations.get(record.foundationId);
if (previousRecord && previousFoundation
&& guardianRecordsMatch(previousRecord, record)
&& guardianRecordMatchesFoundation(record, previousFoundation)) {
foundationsById.set(record.foundationId, verifiedFoundationForManifest(previousFoundation, record, manifest));
} else {
changedRecords.push(record);
}
}
if (changedRecords.length) {
const module = await loadChainModule();
if (typeof module.loadBuildSitesByIds !== "function") {
throw new Error("BuildSite batch loader is unavailable.");
}
const loaded = await module.loadBuildSitesByIds(changedRecords.map((record) => record.foundationId));
const byId = new Map((loaded ?? []).map((foundation) => [String(foundation.foundationId), foundation]));
for (const record of changedRecords) {
const foundation = byId.get(record.foundationId);
if (!foundation || !guardianRecordMatchesFoundation(record, foundation)) {
throw new Error(`Guardian foundation ${record.foundationId} failed BuildSite verification.`);
}
foundationsById.set(record.foundationId, verifiedFoundationForManifest(foundation, record, manifest));
}
}
requireManifestStillCurrent(manifest, entry);
const foundations = records.map((record) => foundationsById.get(record.foundationId));
const verified = await cache?.putVerifiedRegion?.({ ...manifest, records }, foundations) ?? {
...manifest,
foundations,
verified: true,
};
requireManifestStillCurrent(manifest, entry);
regionStates.set(regionKey(manifest), verified);
for (const foundation of foundations) pendingFoundations.delete(String(foundation.foundationId));
return verified;
}
Region identity and chain-derived metadata are checked before this function. It then rejects duplicates and out-of-region records, reloads changed BuildSites, verifies the fields represented by BuildSite, and repeats the comparison against the current resolver entry around the cache write. Those checks do not compare the record content hash with BuildingManifest and read in-memory resolver metadata rather than issuing a fresh chain RPC request.
IMPLEMENTATION EVIDENCE
Where these claims come from
Each claim is intentionally scoped to a concrete implementation path. These references are for verification, not decoration.
programs/nicechunk_guardian/src/lib.rs
Defines the accepted instruction tags, signer and account gates, cardinal-neighbor validation, direct NCK treasury transfer, endpoint updates, Building-PDA blueprint authority, and governance operator rotation.
programs/nicechunk_guardian/src/state.rs
Defines the 100-Chunk Region model, correct 160-byte Registry counters and Genesis flag, 288-byte Region layout, reserved proof fields, and monotonic blueprint metadata update.
programs/nicechunk_guardian/src/cluster_config.rs
Pins the core, NCK mint, treasury/governance, and Building Program identities used by Guardian validation.
sdk/nicechunk-guardian.ts
Derives Registry, treasury, Region, and four cardinal-neighbor PDAs; constructs registration and endpoint instructions; and decodes the intended account layouts.
scripts/list-guardians.ts
Enumerates fixed-size Region accounts directly and filters active records because aggregate Registry counters do not contain coordinates or endpoint metadata.
guardian/guardian.js
Reads Region accounts for the public registry map, builds wallet-signed registration and endpoint transactions, and demonstrates that displayed active nodes are registry records.
Guardian/README.md
States the relay-only authority boundary, default 201 x 201 service window, 15 x 15 AOI, binary protocol, node-owned endpoint model, and local operational scope.
Guardian/src/config.h
Defines default service radius, AOI radius, position precision, connection limits, frame-rate limits, payload bound, and backpressure configuration.
Guardian/config/guardian.example.conf
Exposes the operator-configurable defaults that are independent of a 100 x 100 chain assignment.
Guardian/src/chunk.cpp
Implements inclusive runtime service-range checks, local Chunk encoding, and stable row-major topic indexes for the configured square.
Guardian/src/aoi.cpp
Builds the square AOI offsets whose count is `(2r + 1)^2`.
Guardian/src/protocol.h
Defines protocol V1 message fields and exact sizes, including the unsigned 52-byte HELLO and compact movement and DIG batch records.
Guardian/src/protocol.cpp
Decodes wallet bytes, starting Chunk, and client nonce without a signature or challenge field.
Guardian/src/app.cpp
Accepts a nonzero claimed wallet, applies frame-level rate and range filters, relays realtime events, and permits connected clients to submit raw building announcements.
Guardian/src/guardian.cpp
Maintains transient players and Chunk rooms, coalesces movement into AOI batches, validates local building-record geometry, and emits the current V3 truncated SHA-256 manifest.
play/play-guardian-registry.js
Derives exact nine-Region PDA neighborhoods, performs confirmed batch reads and strict account decoding, caches results, and constructs chain-discovered endpoints.
play/play-guardian-client.js
Builds the unsigned HELLO, encodes binary batches, implements bounded reconnect backoff, and verifies V1, V2, and V3 Guardian manifest hashes according to their parsed versions.
play/play-guardian.js
Disables realtime on a current-Region cache miss, relays transient avatar state, installs unsigned action-3 DIG hints as protected chain deltas, and keeps local play available while Guardian is offline.
play/main.js
Sends pending and confirmed mining hints at different lifecycle points, then invalidates and reloads authoritative Chunk deltas.
scripts/sync-guardian-blueprint.ts
Accepts V2 and V3 manifests, recomputes their version-specific digest, verifies every record against a BuildSite and each active-revision record against a BuildingManifest PDA, and sends an authorized publish transaction unless its hash-only unchanged branch returns early.
programs/nicechunk_building/src/lib.rs
Restricts blueprint publication to governance or the dedicated publisher and signs the Guardian CPI with the Building Program's derived authority.
play/play-chain-foundations.js
Requires chain-discovered source identity and digest metadata, verifies changed BuildSites, checks freshness around cache writes, and exposes only verified regional records.
play/play-building-cache.js
Scopes IndexedDB to the guardian-blueprint-v3 schema, reuses only explicitly verified entries, and bounds Region and building record counts.
play/play-chain-buildings.js
Loads BuildingManifest PDAs and requires each full 32-byte content hash to match the 16-byte prefix carried by its BuildSite-derived regional record before rendering or building-cache reuse.
docs/guardian-load-audit.md
Records deterministic core load-test evidence and its limit: the fixture is not a network soak test or universal production-capacity guarantee.
