CONNECTION AND ADDRESS BOUNDARY
Connect shares a public address before the wallet signs anything
When a player presses Connect, Seed selects the first supported injected wallet it detects and calls provider.connect. After approval, the provider returns a public key. The page records that address under the Seed-specific local key and also writes the site's shared wallet binding. It then performs a public GET status query for that address. The server can therefore see which address is being checked before the player requests or signs a challenge.
That connection step does not export the external wallet's private key, sign a message, create a transaction, pay a fee, or prove continuing control. A browser localStorage address can also survive a reload while no wallet provider is connected. The current corrected state logic distinguishes a cached address, an active provider connection, and a registration confirmed by a successful status or join response for the exact same wallet. A local cache is still editable browser data, never independent proof.
The page tries to detect or switch the wallet toward Solana Devnet. Some providers cannot report a network or support switching; the network helper can explicitly return unknown and unverified. The Seed page now presents that result as not verified. More importantly, this registration API never queries a Solana RPC, and the later message contains no cluster or mint. A Devnet interface preference must not be described as an on-chain Devnet registration.
The status endpoint is public and requires no wallet signature. Anyone who knows an address can ask for the registration ID, campaign, wallet label, central status, planned allocation metadata, explicit not-determined eligibility status, not-implemented distribution status, and timestamps that endpoint returns. Guardian relay and deterministic world reconstruction do not participate in connection, challenge signing, central registration, or payment evidence.
It does not return the external wallet's private key or approve the later message.
The current code writes nicechunk.seed.wallet and the shared nicechunk.walletAddress, walletName, and walletBoundAt fields.
This happens after connection and before challenge signing; the endpoint itself does not demand proof of wallet control.
None authenticates this connection or turns it into shared game or token state.
CHALLENGE AND SIGNATURE BOUNDARY
The wallet signs one exact message, then browser and server verify it separately
The central API validates that the submitted wallet decodes to 32 bytes, creates a URL-safe random nonce from 24 random bytes, records the server time, and builds the message line by line. The default challenge lifetime is 600 seconds, subject to the server's environment setting. The challenge row is written immediately with wallet, full message, issue and expiry times, IP string, and User-Agent, even if the player later cancels the wallet prompt.
Before approving, read the complete wallet message: registration ID, wallet, nonce, issue time, origin, purpose, the expanded no-transaction and no-purchase terms, the planned free-allocation policy, and the statement that current registration is only a central record. The origin line is supplied by client JSON and is not currently checked against a trusted request Host or Origin header. Wallet name is not part of the signed text. Signing therefore proves agreement with the literal bytes, not that every printed metadata field was independently authenticated by the server.
The browser encodes the returned message with TextEncoder, asks provider.signMessage to sign those bytes, converts the result to 64 signature bytes, and verifies it with TweetNaCl and the wallet public key. Only after that local check does it POST wallet, wallet name, nonce, full message, and Base58 signature. The server then loads the saved nonce, rejects a missing, used, expired, wallet-mismatched, message-mismatched, or invalidly signed challenge, and runs Ed25519 verification again.
A valid signature means the corresponding private key produced a signature for those exact bytes. When the server also accepts an unexpired challenge, the submission reached that service within the challenge window; the signature itself contains no trusted timestamp. It supports the central service's registration decision. It does not identify a unique human, prove permanent ownership, create a Solana transaction, include instructions or accounts, pay a fee, produce a blockhash or explorer receipt, call a program, or move funds. Message signatures can still matter wherever another system accepts the same signed message, so read them rather than treating them as harmless decoration.
They separate attempts and limit ordinary replay of one stored challenge; they do not enforce one human per wallet.
It proves signature, exact message, and public key match locally, not that the API committed a record.
The central service additionally checks nonce existence, use, expiry, wallet equality, and exact stored message equality.
A 64-byte detached signature may look technical, but this path never becomes a Solana transaction.
- Match the campaign and wallet
Confirm that the campaign text is expected and that the full wallet address matches the account you intended to use.
- Read nonce, time, origin, and purpose
Cancel if any line is missing or unexpected. Remember that the current origin is a signed string, not a server-authenticated domain proof.
- Confirm the request type
The wallet should present a readable message signature, not a transaction, token approval, SOL transfer, or request for a secret.
- Reject and restart if unsure
A rejected or expired challenge can be replaced with a fresh one. Never approve bytes you cannot inspect or explain.
CENTRAL STORAGE AND PRIVACY BOUNDARY
Registered is a maintainable SQLite record, not an immutable chain account
The service has two central tables. A challenge row stores nonce, wallet, exact message, issued_at, expires_at, optional used_at, IP, and User-Agent. A whitelist row stores an auto-increment ID, campaign ID, globally unique wallet, wallet name, signature, exact message, unique nonce, central status, planned_allocation_nck, allocation_status, IP, User-Agent, created_at, and updated_at. It does not store the private key, eligibility decision, distribution receipt, or token balance. Nothing in either table is a Solana PDA or token account.
After server verification, a new wallet inserts one whitelist row. A wallet that signs again updates its existing row with new wallet name, signature, message, nonce, status, IP, User-Agent, and update time while preserving the original ID and created_at. The current update forces status back to registered. The unique constraint is wallet alone rather than campaign plus wallet, and the update does not replace campaign_id even though the response reports the current configured campaign. Those details matter before this database can safely represent multiple campaigns or later settlement states.
The public status endpoint looks up a row by wallet without asking for a signature and returns a limited projection. The browser also caches an entry summary. The corrected client now trusts only a successful status or join response whose entry wallet exactly matches the current wallet; a null result, failure, disconnect, or wallet switch removes the displayed confirmation. It does not require the returned status to equal registered or compare a campaign ID. Its confirmation therefore means only that the central service returned a same-address entry through status or join, not that the entry belongs to the current campaign or establishes qualification, settlement, or payment. This prevents an old or forged local cache from appearing registered, but the resulting screen is still a central-service observation, not independent or on-chain proof.
Privacy begins before signing: challenge creation already stores request metadata. The repository shows no challenge cleanup job, user deletion path, or stated retention period. User-Agent is client-controlled, and the service takes the first X-Forwarded-For value as IP, so that stored string is not reliable proof of a unique person or source address. An operator can maintain or change the SQLite database. Registered therefore means only that this central service currently has a row it reports as registered.
The row exists before the wallet signs and can remain even when the player rejects the prompt.
The private key is not stored, but the signed statement and identifying request data are.
It preserves the original ID and created time, overwrites later proof fields, and currently resets status to registered.
No signature is required to read the projected campaign, label, central status, planned-policy fields, eligibility status, distribution status, and times.
The current repository also has no Seed schema migration or reproducible service unit tied to the static build.
TOKEN AND PAYMENT EVIDENCE BOUNDARY
A displayed 100 NCK value is not NCK in the wallet
The current page labels 100 NCK as a planned free allocation before a wallet connects. The SQLite schema stores planned_allocation_nck with a default integer of 100 and allocation_status = planned_policy_not_entitlement. Join and status responses expose plannedAllocationNck together with eligibilityStatus = not_determined and distributionStatus = not_implemented. Those are central policy and status fields, not entitlement or token evidence. The whitelist path does not identify an NCK mint in its code, import the SPL Token library, derive a token account, read a token balance, build a mint or transfer instruction, submit an RPC transaction, store a transaction signature, or wait for confirmation. Its displayed 0.01 SOL policy reference also has no purchase or SOL-payment path here.
The source has no implemented first-batch size, ordering rule, campaign end, qualification algorithm, anti-abuse decision, settlement state machine, or payout worker. The database has registered but no code path that establishes qualified, settled, transferred, or received. Campaign copy can describe an intended allocation, but it cannot be presented as guaranteed, enforceable, already sent, or visible in a wallet.
A real NCK check must start with the correct cluster and exact mint. The current Devnet NCK mint is HSnWF5kjkWVrceW2SaSskScuLveUZE4gpthZ2ZXRPQPo and uses six decimals. One hundred whole NCK would therefore equal 100,000,000 base units. Even that calculation is not payment evidence: the receiver needs a token account owned by the registered wallet for that mint, a successful Token Program transfer, a transaction signature and slot at a stated commitment, and a resulting balance observation.
The Marketplace program shows what an actual NCK movement path looks like: it checks the NCK mint, Token Program, and source and destination token-account owners, reads and splits the listing's already stored base-unit price, calls transfer_checked, and invokes the Token Program. The client performs the earlier whole-token-to-base-unit conversion when a listing price is created. Seed calls none of either path. The same public address can exist on Devnet and Mainnet Beta while holding different balances on each ledger, so an address or mint existing somewhere is not enough.
A central SQLite row exists and may contain planned_allocation_nck = 100 with a non-entitlement policy status. Eligibility remains not determined and distribution remains not implemented.
This exact mint belongs to the chain implementation outside Seed; the Seed schema and message do not bind to it.
Six decimals define this arithmetic. A number with correct units still does not prove a transfer occurred.
All must match the registered wallet and stated cluster before saying NCK was received.
Neither relay messages nor terrain generation can prove an SPL token payment.
- Name the cluster and mint
Do not accept the word NCK alone. Verify the intended Solana cluster and the complete mint address.
- Find the wallet-owned token account
Confirm that the token account uses that mint and that its owner is the registered wallet address.
- Inspect a successful transfer
A real receipt names a transaction signature, Token Program instruction, source, destination, amount in base units, slot, and commitment.
- Read the resulting on-chain balance
Compare the correct token account before and after. A campaign card, database number, or browser cache cannot replace this observation.
FOLLOW THE PROOF
Eight excerpts separate address, signed bytes, central storage, and token movement
The formulas state the exact acceptance condition and the payment evidence that is absent. The verbatim excerpts show connection and status lookup, challenge construction, browser signing, server verification, SQLite fields, repeat registration, corrected local-state trust, and a real SPL transfer path used only as a contrast.
The exact challenge message
message = title + campaign + wallet + nonce + issuedAt + clientSuppliedOrigin + purpose + noTransactionStatementThe server joins these fields with newline characters and the wallet signs the resulting UTF-8 bytes. Wallet name, IP, User-Agent, Solana cluster, NCK mint, token account, and reward amount are not in the signed text. Origin appears in the bytes but is currently copied from client JSON rather than authenticated against the request host.
- nonce
- A fresh URL-safe token generated from 24 random bytes and stored as the challenge key.
- issuedAt
- A server-created UTC text time; expiry is stored separately as an integer timestamp.
- clientSuppliedOrigin
- The literal client request-body string. It is signed text, not independently verified origin evidence.
- +
- Means exact ordered concatenation with newline separators, not arithmetic addition.
What the central service accepts
accepted = exists(nonce) AND unused AND now <= expiresAt AND storedWallet = wallet AND storedMessage = message AND Ed25519Verify(wallet, UTF8(message), signature)This is the implemented server acceptance boundary. The browser performs a similar cryptographic check first, but only the server's checks lead to its SQLite write. The expression does not contain a Solana cluster, RPC, program, account, transaction, fee, confirmation, qualification, anti-abuse decision, or token transfer.
- exists(nonce)
- A matching challenge row must already exist in central SQLite.
- unused
- The row's used_at field must be empty at the time it is read. The current later update is not an atomic compare-and-set.
- storedMessage = message
- Every byte represented by the submitted text must match the server's saved challenge text.
- Ed25519Verify
- Cryptographic verification of public key, exact UTF-8 bytes, and detached signature.
Registration metadata is not payment evidence
registered AND planned_allocation_nck = 100 AND allocation_status = planned_policy_not_entitlement != eligible != distributed != SPL transfer != wallet token balance increasedThe left side is central database state. The right-side observations would require explicit campaign decisions and then matching Solana evidence. With six decimals, 100 whole NCK equals 100,000,000 base units, but arithmetic and display text cannot prove that those units moved.
- registered
- The central whitelist row currently reports status registered.
- planned_allocation_nck = 100
- A central planned-policy integer, paired with a non-entitlement status and no mint, eligibility decision, token account, transfer signature, or balance.
- SPL transfer
- A successful Token Program instruction moving base units between validated token accounts for the exact mint.
- wallet token balance increased
- A later observation of the correct wallet-owned token account on the stated cluster.
- !=
- Means the two states are not interchangeable and one must not be used as evidence for the next.
Connection stores the address and immediately checks central status
JavaScriptseed/seed.jsconst result = await wallet.provider.connect();
const publicKey = publicKeyToString(result?.publicKey || wallet.provider.publicKey);
if (!publicKey) throw new Error(text("status.actions.missingPublicKey"));
const walletChanged = publicKey !== seedState.wallet;
seedState = transitionSeedWallet(seedState, publicKey);
seedState = {
...seedState,
provider: wallet.provider,
walletName: wallet.name,
error: "",
};
bindSeedWalletEvents(wallet.provider, wallet.name);
if (walletChanged) clearStoredSeedEntry();
localStorage.setItem(seedWalletStorageKey, publicKey);
await ensureSeedWalletNetwork(wallet.provider);
persistConnectedWallet({ walletAddress: publicKey, walletName: wallet.name });
await refreshEntryStatus(publicKey);
This current client excerpt begins after the player chose Connect. It accepts a public key, changes the local state, stores the Seed address, applies the shared wallet binding, and queries the central status endpoint. It does not sign a message or transaction here.
- 1-3
Ask the external provider to connect, convert the returned public key to text, and reject a missing address.
- 4-11
Detect an account change, clear prior registration trust through the state transition, attach the live provider, and listen for later account or disconnect events.
- 12-17
Clear a stale entry when needed, store the address, attempt the client network policy, update the shared binding, and query the central registration by public address.
The server creates and stores the exact challenge
Pythonseed_api.pynow = int(time.time())
nonce = secrets.token_urlsafe(24)
issued_at = utc_now_iso()
message = "\n".join([
"NiceChunk Player Whitelist Registration",
f"Registration ID: {CAMPAIGN_ID}",
f"Wallet: {wallet}",
f"Nonce: {nonce}",
f"Issued At: {issued_at}",
f"Origin: {origin}",
"Purpose: Prove control of this wallet and submit a free player-whitelist registration.",
"This signature does not authorize a transaction, transfer funds, purchase NCK, reserve a paid allocation, or make an investment.",
"Registration requires no payment. The planned initial 100 NCK allocation for eligible whitelisted players is free.",
"Current registration stores only a central record; it does not determine eligibility or transfer NCK.",
])
with sqlite3.connect(DB_PATH) as db:
db.execute(
"INSERT INTO seed_challenges(nonce,wallet,message,issued_at,expires_at,ip,user_agent) VALUES(?,?,?,?,?,?,?)",
(nonce, wallet, message, now, now + CHALLENGE_TTL_SECONDS, client_ip(self), self.headers.get("user-agent", "")[:300]),
)
The server creates freshness data, constructs the complete readable message, and writes a central challenge row before a wallet signs. The origin variable was read from client JSON earlier; this excerpt does not authenticate it against the request host.
- 1-3
Capture integer server time, generate a random URL-safe nonce from 24 random bytes, and format a UTC issue time.
- 4-13
Join the title, registration ID, wallet, nonce, issue time, client-supplied origin, purpose, no-transaction and no-purchase terms, planned free-allocation policy, and central-record-only boundary into exact newline-separated text.
- 14-18
Store nonce, wallet, exact message, issue and expiry times, IP string, and User-Agent in SQLite before returning the challenge.
The browser signs exact bytes and verifies before join
JavaScriptseed/seed.jsconst encodedMessage = new TextEncoder().encode(challenge.message);
const signResult = await seedState.provider.signMessage(encodedMessage, "utf8");
const signature = signatureToBytes(signResult);
if (!signature?.length) throw new Error(text("status.actions.missingSignature"));
if (!nacl.sign.detached.verify(encodedMessage, signature, bs58.decode(seedState.wallet))) {
throw new Error(text("status.actions.localVerificationFailed"));
}
setSeedMessage(
text("status.actions.submittingState"),
text("status.actions.submittingMessage"),
);
const result = await apiPost("/api/seed/join", {
wallet: seedState.wallet,
walletName: seedState.walletName || selectedWallet?.name || "Solana Wallet",
nonce: challenge.nonce,
message: challenge.message,
signature: bs58.encode(signature),
});
TextEncoder produces the bytes the wallet signs. TweetNaCl verifies those same bytes with the current public key. Only then are the wallet, unsigned wallet label, nonce, full message, and Base58 signature sent to the central join endpoint.
- 1-4
Encode the API message as UTF-8, request signMessage, normalize the wallet result, and require nonempty signature bytes.
- 5-7
Verify message, detached signature, and decoded wallet public key locally; stop before join when they do not match.
- 8-15
Report local verification and submit the central registration inputs. No transaction object, instruction, fee payer, or RPC submission appears.
The server enforces challenge state and Ed25519
Pythonseed_api.pynow = int(time.time())
with sqlite3.connect(DB_PATH) as db:
db.row_factory = sqlite3.Row
challenge = db.execute("SELECT * FROM seed_challenges WHERE nonce=?", (nonce,)).fetchone()
if not challenge:
return json_response(self, 400, {"ok": False, "error": "challenge_not_found"})
if challenge["used_at"]:
return json_response(self, 409, {"ok": False, "error": "challenge_used"})
if challenge["expires_at"] < now:
return json_response(self, 400, {"ok": False, "error": "challenge_expired"})
if challenge["wallet"] != wallet or challenge["message"] != message:
return json_response(self, 400, {"ok": False, "error": "challenge_mismatch"})
try:
Ed25519PublicKey.from_public_bytes(public_key).verify(signature_bytes, message.encode("utf-8"))
except InvalidSignature:
return json_response(self, 401, {"ok": False, "error": "signature_invalid"})
The central service makes its own decision. It loads the nonce, checks normal replay and expiry conditions, requires wallet and full message equality, and verifies the detached signature. Passing this block is still only permission to continue into the central database write.
- 1-4
Read current server time and load the complete challenge row by its parameter-bound nonce.
- 5-12
Return separate missing, used, and expired errors; a wallet mismatch or message mismatch shares the same challenge_mismatch error.
- 13-16
Verify the 64-byte signature over the submitted UTF-8 message with the submitted wallet public key and reject an invalid signature.
SQLite stores proof, request metadata, and planned-policy fields
SQLseed_api.pyCREATE TABLE IF NOT EXISTS seed_whitelist_entries (
id INTEGER PRIMARY KEY AUTOINCREMENT,
campaign_id TEXT NOT NULL,
wallet TEXT NOT NULL UNIQUE,
wallet_name TEXT,
signature TEXT NOT NULL,
message TEXT NOT NULL,
nonce TEXT NOT NULL UNIQUE,
status TEXT NOT NULL DEFAULT 'registered',
planned_allocation_nck INTEGER NOT NULL DEFAULT 100,
allocation_status TEXT NOT NULL DEFAULT 'planned_policy_not_entitlement',
ip TEXT,
user_agent TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
)
This table is the central registration authority for the whitelist flow. It stores a planned-policy quantity and an explicit non-entitlement allocation status. It has no mint, token account, base-unit amount, eligibility decision, payout transaction, slot, commitment, or token balance field.
- 1-4
Create a central table with an auto-increment ID, campaign label, and wallet that is globally unique across all campaigns.
- 5-8
Store the wallet label, detached signature, exact signed message, and a unique nonce.
- 9-10
Default the central status to registered and the central reward metadata integer to 100.
- 11-15
Store IP, User-Agent, creation time, and update time. No source retention or user-deletion rule appears in the schema.
Repeat registration updates one row and consumes the challenge
Pythonseed_api.pyexisting = db.execute("SELECT id,created_at FROM seed_whitelist_entries WHERE wallet=?", (wallet,)).fetchone()
if existing:
db.execute(
"UPDATE seed_whitelist_entries SET wallet_name=?, signature=?, message=?, nonce=?, status='registered', ip=?, user_agent=?, updated_at=? WHERE wallet=?",
(wallet_name, signature, message, nonce, client_ip(self), self.headers.get("user-agent", "")[:300], created_at, wallet),
)
entry_id = existing["id"]
first_created = existing["created_at"]
else:
cursor = db.execute(
"INSERT INTO seed_whitelist_entries(campaign_id,wallet,wallet_name,signature,message,nonce,ip,user_agent,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?)",
(CAMPAIGN_ID, wallet, wallet_name, signature, message, nonce, client_ip(self), self.headers.get("user-agent", "")[:300], created_at, created_at),
)
entry_id = cursor.lastrowid
first_created = created_at
db.execute("UPDATE seed_challenges SET used_at=? WHERE nonce=?", (now, nonce))
db.commit()
A known wallet is updated rather than inserted again. The current update replaces proof and request metadata, resets status to registered, preserves the original ID and created time, and does not update campaign_id. It then marks the challenge used and commits these central writes.
- 1-8
Look up by wallet alone; for an existing row overwrite wallet label, proof, nonce, status, IP, User-Agent, and update time while keeping the first ID and creation time.
- 9-15
For a new wallet insert the current campaign and proof fields, then save the new row ID and first creation time.
- 16-17
Mark the challenge used and commit. The read-check and used update are not one compare-and-set statement, so this is not proof of exactly-once behavior under concurrency.
The corrected browser trusts only matching server responses
JavaScriptseed/seed-state.jsexport function confirmSeedEntry(state, entry, source) {
const cleared = clearSeedEntryConfirmation(state);
if (!confirmedEntrySources.has(source) || !entryMatchesSeedWallet(entry, state.wallet)) {
return cleared;
}
return {
...cleared,
entry,
entryConfirmation: source,
statusCheck: source === seedEntryConfirmation.status ? "confirmed" : state.statusCheck,
};
}
export function applySeedStatusPayload(state, payload) {
if (payload?.ok !== true) return clearSeedEntryConfirmation(state, "failed");
if (!payload.entry) return clearSeedEntryConfirmation(state, "empty");
const confirmed = confirmSeedEntry(state, payload.entry, seedEntryConfirmation.status);
return isSeedRegistrationConfirmed(confirmed)
? confirmed
: clearSeedEntryConfirmation(confirmed, "failed");
}
The earlier page could present any local entry as registered, including an entry for another wallet. The corrected pure state transition clears old trust first and accepts only status or join sources whose entry wallet exactly matches the current wallet. This improves the display but does not turn the central response into chain evidence.
- 1-5
Clear all previous registration confirmation and refuse an untrusted source or an entry whose wallet differs from the current wallet.
- 6-11
Retain the entry only with its trusted response source and record a confirmed status lookup when appropriate.
- 14-20
Treat failed and empty status payloads as no confirmation; route a nonempty response through exact-wallet matching and clear it again if that test fails.
A real SPL movement invokes transfer_checked
Rustprograms/nicechunk_market/src/lib.rsfn transfer_nck<'a>(
source_token: &AccountInfo<'a>,
destination_token: &AccountInfo<'a>,
nck_mint: &AccountInfo<'a>,
owner: &AccountInfo<'a>,
token_program: &AccountInfo<'a>,
amount: u64,
) -> ProgramResult {
let ix = spl_token::instruction::transfer_checked(
token_program.key,
source_token.key,
nck_mint.key,
destination_token.key,
owner.key,
&[],
amount,
NCK_DECIMALS,
)
.map_err(|_| NicechunkMarketError::InvalidInstruction)?;
invoke(
&ix,
&[
source_token.clone(),
nck_mint.clone(),
destination_token.clone(),
owner.clone(),
token_program.clone(),
],
)
}
This Marketplace helper is a comparison, not part of Seed. Its caller first validates the exact NCK mint, Token Program, and token-account owners. The helper then builds a checked transfer with source, destination, mint, authority, amount, and decimals and invokes the Token Program. Seed contains no corresponding call.
- 1-8
Require explicit source token account, destination token account, mint, signing owner, Token Program, and integer base-unit amount.
- 9-18
Build transfer_checked with those accounts, the amount, and NCK_DECIMALS so the instruction is bound to the mint's unit scale.
- 19-29
Invoke the Token Program with all required accounts. A transaction must still execute successfully before any later balance claim.
IMPLEMENTATION EVIDENCE
Where these claims come from
Each claim is intentionally scoped to a concrete implementation path. These references are for verification, not decoration.
seed/seed.js
Implements wallet detection and connection, Seed and shared browser bindings, challenge request, signMessage, local TweetNaCl verification, central join, public status refresh, and the corrected registration display boundary.
seed/seed-state.js
Defines pure state transitions that separate cached addresses, active providers, matching status or join responses, empty status, failures, wallet changes, and unverified network results.
seed/tests/seed-state.test.mjs
Covers seven focused cases for untrusted cache entries, exact wallet matching, trusted response sources, wallet switching, empty or failed status, cached versus connected state, and unverified networks.
scripts/audit-seed-flow.mjs
Runs ten mock Chromium scenarios for forged cache data, empty and failed status responses, wallet changes, disconnects, stale responses, unknown or observable networks, and a permanently pending status request without generating or printing a private key.
seed/index.html
Contains the player-whitelist presentation whose planned free allocation and separate future treasury-rate reference must remain distinct from current registration and token evidence.
seed_api.py
Defines central service configuration, challenge and whitelist schemas, challenge message construction, Ed25519 verification, registration upsert, public status, admin listing, stored request metadata, and explicit planned-allocation, not-determined eligibility, and not-implemented distribution fields.
nginx.site.conf
Proxies the public Seed and invite API routes to the local Python service on port 8787, establishing the central-service deployment boundary.
src/solanaNetwork.js
Shows the client-required Devnet policy and the explicit unknown or unverified branch when a provider cannot report or switch network.
src/walletSession.js
Defines the shared browser wallet address, name, and binding-time fields that Seed connection also updates.
programs/nicechunk_market/src/cluster_config.rs
Names the cluster-specific NCK mint constants, including the current Devnet mint used for comparison with a real token path.
programs/nicechunk_market/src/lib.rs
Shows an actual NCK payment path validating the mint, Token Program, and token accounts before invoking SPL transfer_checked. Seed does not call it.
programs/nicechunk_core/src/state.rs
Defines six NCK decimals and the genesis supply in base units, supporting the unit calculation without proving any player payment.
src/chain/nicechunkChain.js
The gameplay chain adapter imports Solana and SPL Token primitives and names the Devnet NCK mint; the absence of this machinery from Seed is the relevant contrast.
docs/wallet-flow-audit.md
Documents browser wallet binding and local-state cleanup as background context; it does not audit Seed signMessage or the central Seed API.
docs/player-guide-architecture.md
Assigns this page ownership of the signed-message, central-registration, privacy, and displayed-reward-versus-payment boundary.