/api/wallet/encryption/key-struct
GETReturns the Encryption Key Structure - a special 25-element array showing which coin's Authentication Numbers (ANs) are used for encrypted RAIDA communications per RAIDA server.
Description
The /api/wallet/encryption/key-struct endpoint reveals the internal encryption key structure used by CloudCoin Console for secure RAIDA communications. When you send encrypted requests to RAIDA servers, each of the 25 RAIDAs requires a specific Authentication Number (AN) from a coin in your wallet to decrypt the request.
This endpoint returns a detailed map showing:
- Which coin provides the AN for each RAIDA (serial number and denomination)
- The actual AN used for that RAIDA (16 bytes as 32 hex characters)
- The encryption mode - whether keys are healthy ("bank"), fractured ("fracked"), or missing ("none")
The encryption key structure is critical for debugging encrypted operations. It answers questions like:
- "Why is encrypted Echo failing on RAIDA 5?" - Check if the coin providing RAIDA 5's AN is valid
- "Are my encryption keys fracked?" - Look at the mode: "bank" = healthy, "fracked" = needs fixing
- "Which coin do I need to fix to restore encryption?" - See which serial numbers appear in the key struct
IMPORTANT: Authentication Numbers (ANs) are highly sensitive cryptographic secrets. The ANs returned by this endpoint are equivalent to passwords that control your coins. This endpoint should only be used for:
- Debugging: Diagnosing encryption issues with RAIDA communications
- Internal tools: Administrative or development purposes only
- Secure environments: Never expose this data over unsecured connections
Always protect the API response containing ANs. Do not log, transmit, or display ANs in production systems.
Encryption Modes
The endpoint automatically detects and reports one of three encryption modes based on the wallet's current state:
Mode 1: Bank Mode (Ideal)
Indicator: "mode": "bank"
Meaning: All 25 RAIDAs use the same coin's ANs for encryption. This is the best-case scenario.
Characteristics:
- All 25 RAIDAs have identical
serial_numberanddenomination - The coin is randomly selected from the Bank folder
- Encrypted operations work reliably across all RAIDAs
- No encryption key loss - wallet is healthy
Action Required: None - encryption keys are in optimal state
Mode 2: Fracked Mode (Needs Attention)
Indicator: "mode": "fracked"
Meaning: Different RAIDAs use ANs from different coins. The encryption key structure is fragmented across multiple coins.
Characteristics:
- RAIDAs have different
serial_numberordenominationvalues - ANs are sourced from multiple coins in the Fracked folder
- Indicates past POWN operations only partially succeeded
- Encrypted operations may fail inconsistently across RAIDAs
Action Required: Run /api/program/fix-encryption to consolidate encryption keys back to a single coin
Mode 3: None (Critical)
Indicator: "mode": "none"
Meaning: No coins are available to provide encryption keys. Encrypted operations cannot work.
Characteristics:
- All
serial_numbervalues are 0 - All
denominationvalues are 0 - No coins in Bank or Fracked folders
- All encrypted RAIDA requests will fail
Action Required: Import coins into the wallet or recover from backup. Without coins, encryption is not possible.
Parameters
This endpoint does not require any parameters.
Response
Returns a JSON object containing the encryption key structure with 25 elements (one per RAIDA server).
Response Properties
- raida_id (integer): RAIDA server ID (0-24)
- denomination (integer): Denomination of the coin providing the AN (-8 to 11)
- serial_number (integer): Serial number of the coin providing the AN
- an (string): The 16-byte Authentication Number as 32 hex characters
Example Response (Bank Mode)
{
"status": "success",
"operation": "encryption-key-struct",
"mode": "bank",
"message": "Using random coin from Bank folder",
"key_struct": [
{
"raida_id": 0,
"denomination": 3,
"serial_number": 12345,
"an": "a1b2c3d4e5f67890a1b2c3d4e5f67890"
},
{
"raida_id": 1,
"denomination": 3,
"serial_number": 12345,
"an": "f1e2d3c4b5a67890f1e2d3c4b5a67890"
},
{
"raida_id": 2,
"denomination": 3,
"serial_number": 12345,
"an": "0123456789abcdef0123456789abcdef"
},
...
{
"raida_id": 24,
"denomination": 3,
"serial_number": 12345,
"an": "fedcba9876543210fedcba9876543210"
}
]
}
Note: In Bank mode, all 25 RAIDAs have the same serial_number (12345) and denomination (3 = 100 CloudCoin). Each RAIDA uses a different AN from the same coin (coins have 25 ANs, one per RAIDA).
Example Response (Fracked Mode)
{
"status": "success",
"operation": "encryption-key-struct",
"mode": "fracked",
"message": "Using combined good ANs from Fracked coins",
"key_struct": [
{
"raida_id": 0,
"denomination": 3,
"serial_number": 12345,
"an": "a1b2c3d4e5f67890a1b2c3d4e5f67890"
},
{
"raida_id": 1,
"denomination": 4,
"serial_number": 67890,
"an": "f1e2d3c4b5a67890f1e2d3c4b5a67890"
},
{
"raida_id": 2,
"denomination": 3,
"serial_number": 12345,
"an": "0123456789abcdef0123456789abcdef"
},
...
{
"raida_id": 24,
"denomination": 5,
"serial_number": 99999,
"an": "fedcba9876543210fedcba9876543210"
}
]
}
Note: In Fracked mode, different RAIDAs have different serial_numbers and denominations. RAIDA 0 uses SN 12345, RAIDA 1 uses SN 67890, RAIDA 24 uses SN 99999. This indicates the encryption key structure is fragmented across multiple coins.
Example Response (None Mode)
{
"status": "success",
"operation": "encryption-key-struct",
"mode": "none",
"message": "No coins available for encryption key struct",
"key_struct": [
{
"raida_id": 0,
"denomination": 0,
"serial_number": 0,
"an": "00000000000000000000000000000000"
},
{
"raida_id": 1,
"denomination": 0,
"serial_number": 0,
"an": "00000000000000000000000000000000"
},
...
{
"raida_id": 24,
"denomination": 0,
"serial_number": 0,
"an": "00000000000000000000000000000000"
}
]
}
Note: In None mode, all serial_numbers and denominations are 0. No coins are available in the wallet to provide encryption keys.
Error Responses
500 Internal Server Error
Returned if the system fails to retrieve the encryption key structure.
Example Error Response
{
"status": "error",
"message": "Failed to get encryption key struct"
}
Examples
JavaScript (fetch)
const API_HOST = 'http://localhost:8080';
async function getEncryptionKeyStruct() {
try {
const response = await fetch(`${API_HOST}/api/wallet/encryption/key-struct`);
const result = await response.json();
console.log(`Encryption Mode: ${result.mode}`);
console.log(`Message: ${result.message}`);
// Check if keys are fragmented
if (result.mode === 'fracked') {
console.warn('ā ļø Encryption keys are fracked across multiple coins!');
// Find unique serial numbers
const uniqueSNs = new Set(
result.key_struct.map(item => item.serial_number)
);
console.log(`Using ${uniqueSNs.size} different coins for encryption`);
// Show which RAIDAs use which coins
result.key_struct.forEach(item => {
console.log(`RAIDA ${item.raida_id}: SN ${item.serial_number}, Denom ${item.denomination}`);
});
console.log('š” Run /api/program/fix-encryption to consolidate keys');
} else if (result.mode === 'bank') {
console.log('ā Encryption keys are healthy');
const coin = result.key_struct[0];
console.log(`Using coin SN ${coin.serial_number}, Denom ${coin.denomination}`);
} else if (result.mode === 'none') {
console.error('ā No encryption keys available - wallet has no coins');
}
return result;
} catch (error) {
console.error('Error fetching encryption key struct:', error);
throw error;
}
}
// Example: Debug encrypted Echo failures
async function debugEncryptedEcho() {
console.log('Step 1: Get encryption key structure...');
const keyStruct = await getEncryptionKeyStruct();
console.log('\nStep 2: Test encrypted Echo...');
const echoResponse = await fetch(`${API_HOST}/api/program/echo?encrypted=true`);
const echoResult = await echoResponse.json();
console.log('\nStep 3: Compare results...');
echoResult.raida_responses.forEach((raida, index) => {
if (!raida.success) {
const key = keyStruct.key_struct[index];
console.error(
`RAIDA ${index} failed - using coin SN ${key.serial_number}, ` +
`AN: ${key.an.substring(0, 8)}...`
);
}
});
}
getEncryptionKeyStruct();
cURL
# Get encryption key structure
curl -X GET "http://localhost:8080/api/wallet/encryption/key-struct"
# Get with formatted output using jq
curl -s "http://localhost:8080/api/wallet/encryption/key-struct" | jq '.'
# Check encryption mode only
curl -s "http://localhost:8080/api/wallet/encryption/key-struct" | jq '.mode'
# Count unique serial numbers (detect fracked keys)
curl -s "http://localhost:8080/api/wallet/encryption/key-struct" \
| jq '.key_struct | map(.serial_number) | unique | length'
# Show which coin is used per RAIDA
curl -s "http://localhost:8080/api/wallet/encryption/key-struct" \
| jq '.key_struct[] | "RAIDA \(.raida_id): SN \(.serial_number), Denom \(.denomination)"'
# Example: Full diagnostic script
#!/bin/bash
echo "=== Encryption Key Diagnostic ==="
RESPONSE=$(curl -s "http://localhost:8080/api/wallet/encryption/key-struct")
MODE=$(echo "$RESPONSE" | jq -r '.mode')
echo "Mode: $MODE"
if [ "$MODE" = "fracked" ]; then
echo "ā ļø WARNING: Encryption keys are fracked!"
UNIQUE_COUNT=$(echo "$RESPONSE" | jq '.key_struct | map(.serial_number) | unique | length')
echo "Using $UNIQUE_COUNT different coins"
echo "Run: curl -X GET 'http://localhost:8080/api/program/fix-encryption'"
elif [ "$MODE" = "bank" ]; then
echo "ā Encryption keys are healthy"
SN=$(echo "$RESPONSE" | jq -r '.key_struct[0].serial_number')
echo "Using coin SN: $SN"
elif [ "$MODE" = "none" ]; then
echo "ā ERROR: No encryption keys available"
fi
Go
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
const ApiHost = "http://localhost:8080"
type KeyStructItem struct {
RaidaID int `json:"raida_id"`
Denomination int `json:"denomination"`
SerialNumber int `json:"serial_number"`
AN string `json:"an"`
}
type EncryptionKeyStructResponse struct {
Status string `json:"status"`
Operation string `json:"operation"`
Mode string `json:"mode"`
Message string `json:"message"`
KeyStruct []KeyStructItem `json:"key_struct"`
}
func getEncryptionKeyStruct() (*EncryptionKeyStructResponse, error) {
resp, err := http.Get(fmt.Sprintf("%s/api/wallet/encryption/key-struct", ApiHost))
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("read failed: %w", err)
}
var result EncryptionKeyStructResponse
if err := json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("JSON parse failed: %w", err)
}
return &result, nil
}
func analyzeEncryptionKeys(result *EncryptionKeyStructResponse) {
fmt.Printf("Encryption Mode: %s\n", result.Mode)
fmt.Printf("Message: %s\n", result.Message)
switch result.Mode {
case "fracked":
fmt.Println("ā ļø WARNING: Encryption keys are fracked!")
// Count unique serial numbers
uniqueSNs := make(map[int]bool)
for _, item := range result.KeyStruct {
uniqueSNs[item.SerialNumber] = true
}
fmt.Printf("Using %d different coins for encryption\n", len(uniqueSNs))
// Show breakdown per RAIDA
fmt.Println("\nRAIDA Breakdown:")
for _, item := range result.KeyStruct {
fmt.Printf(" RAIDA %2d: SN %d, Denom %d, AN %s...\n",
item.RaidaID,
item.SerialNumber,
item.Denomination,
item.AN[:16])
}
fmt.Println("\nš” Run /api/program/fix-encryption to consolidate keys")
case "bank":
fmt.Println("ā Encryption keys are healthy")
coin := result.KeyStruct[0]
fmt.Printf("Using coin SN %d, Denomination %d\n",
coin.SerialNumber,
coin.Denomination)
case "none":
fmt.Println("ā ERROR: No encryption keys available")
fmt.Println("Wallet has no coins - import coins to enable encryption")
}
}
func main() {
fmt.Println("Fetching encryption key structure...")
result, err := getEncryptionKeyStruct()
if err != nil {
panic(err)
}
fmt.Println("\n=== Encryption Key Analysis ===")
analyzeEncryptionKeys(result)
// Optional: Save full structure to file for debugging
if result.Mode == "fracked" {
fmt.Println("\nSaving full key structure to key_struct.json...")
jsonData, _ := json.MarshalIndent(result, "", " ")
if err := ioutil.WriteFile("key_struct.json", jsonData, 0644); err != nil {
fmt.Printf("Warning: Failed to save file: %v\n", err)
} else {
fmt.Println("Saved to key_struct.json")
}
}
}
Understanding Authentication Numbers (ANs)
Each CloudCoin contains 25 Authentication Numbers (ANs) - one for each RAIDA server. When you send an encrypted request:
- Client encrypts: Your client uses the AN for RAIDA X to encrypt the request payload using AES-128-CTR
- RAIDA decrypts: RAIDA X uses its stored AN for your coin to decrypt the request
- Verification: If decryption succeeds and CRC matches, the request is authenticated
CloudCoin's security model uses distributed trust - no single RAIDA knows all 25 ANs for a coin. Each RAIDA only knows one AN (its own). This prevents any single RAIDA from compromising the entire coin. The encryption key structure shows which AN from which coin is used for each RAIDA.
Common Use Cases
1. Debugging Encrypted Echo Failures
If encrypted Echo requests fail on specific RAIDAs, check the key structure to see which coin's AN is being used for those RAIDAs. The coin may be fracked or counterfeit on those RAIDAs.
2. Diagnosing Fracked Encryption Keys
Run this endpoint to see if your encryption keys are fragmented. If mode: "fracked", you'll see different serial numbers across RAIDAs. Use /api/program/fix-encryption to consolidate.
3. Verifying Encryption Key Health
After running fix-encryption or importing new coins, check that mode: "bank" and all 25 RAIDAs use the same serial number.
4. Identifying Which Coin to Fix
If encrypted operations are failing, the key struct reveals which coin(s) are providing the ANs. You can then focus on fixing those specific coins in the Fracked folder.
DO NOT expose this endpoint to untrusted clients. The ANs returned are equivalent to private keys for your coins. This endpoint is intended for:
- Administrative dashboards (secured with authentication)
- Development/debugging tools (local environments only)
- Internal monitoring systems (behind firewalls)
In production APIs, consider requiring authentication tokens and rate limiting.
Related Endpoints
/api/program/login
Set the encryption key for accessing encrypted files. Must be called before working with encrypted coins.
/api/program/fix-encryption
Fix fracked encryption keys by consolidating fragmented ANs back to a single coin using RAIDA healing.
/api/program/detect-lost-encryption
Detect which RAIDAs have lost encryption connections by scanning wallet POWN strings.
/api/program/echo
Test RAIDA connectivity with optional encryption. Use ?encrypted=true to test encrypted communications.
Technical Implementation
The /api/wallet/encryption/key-struct endpoint internally calls cc_get_encryption_key_struct() from utils.c, which implements the following algorithm:
// Simplified algorithm from utils.c
cc_result_t cc_get_encryption_key_struct(EncryptionKeyStruct* out_struct) {
// Step 1: Try to find a single Bank coin with all 'p' (pass) status
for each coin in Bank folder {
if (all_25_raidas_have_pass_status(coin)) {
// Bank mode: Use this coin's 25 ANs
for (int raida = 0; raida < 25; raida++) {
out_struct->serial_number[raida] = coin.serial_number;
out_struct->denomination[raida] = coin.denomination;
memcpy(out_struct->ans[raida], coin.ans[raida], 16);
}
return CC_SUCCESS; // Mode: "bank"
}
}
// Step 2: Build key struct from Fracked coins
// For each RAIDA, find the best coin with 'p' status on that RAIDA
for (int raida = 0; raida < 25; raida++) {
CloudCoin* best_coin = NULL;
int best_pass_count = 0;
for each coin in Fracked folder {
if (coin.pown_string[raida] == 'p') {
int pass_count = count_passes(coin.pown_string);
if (pass_count > best_pass_count) {
best_coin = &coin;
best_pass_count = pass_count;
}
}
}
if (best_coin != NULL) {
// Found a coin with 'p' on this RAIDA
out_struct->serial_number[raida] = best_coin->serial_number;
out_struct->denomination[raida] = best_coin->denomination;
memcpy(out_struct->ans[raida], best_coin->ans[raida], 16);
} else {
// No coin found - set to zeros
out_struct->serial_number[raida] = 0;
out_struct->denomination[raida] = 0;
memset(out_struct->ans[raida], 0, 16);
}
}
return CC_SUCCESS; // Mode: "fracked" or "none"
}
The mode is determined by examining the resulting structure:
- Bank: All 25 RAIDAs have the same serial_number and denomination
- Fracked: RAIDAs have different serial_numbers or denominations
- None: All serial_numbers and denominations are 0