/api/recovery/status

GET

Returns the startup recovery status, indicating whether the program has finished its initial coin health checks and is ready for use.

Description

When the CloudCoin Console server starts, it scans all wallet Suspect folders for coins left behind by interrupted deposit operations. These coins need to be recovered (authenticated and graded) before the program is fully ready for use.

The /api/recovery/status endpoint lets the GUI check whether this startup recovery process is still running, has completed, or found no work to do. The GUI should poll this endpoint on startup and show a progress indicator until recovery finishes.

How It Works

On startup, the server:

  1. Scans all wallet Suspect folders for unprocessed coins
  2. Groups coins by their original task IDs (extracted from filenames)
  3. Recovers each group by running POWN and GRADE phases
  4. Moves orphan coins (no task ID) to the Trash folder

The GUI should poll this endpoint until recovering is false and scan_complete is true.

GUI Startup Gate

The GUI should not allow deposit, transfer, export, or other coin operations until recovery is complete. Read-only operations (balance, list, show-coins) are safe to use during recovery.

Parameters

This endpoint does not require any parameters.

Response

Returns a JSON object describing the current recovery state, including per-task progress.

Response Properties

command string
Always "recovery-status".
success boolean
Always true for this endpoint.
recovering boolean
True if recovery is currently in progress. False when idle or complete.
scan_complete boolean
True once the initial scan of Suspect folders has finished. When both scan_complete is true and recovering is false, the program is fully ready.
message string
Human-readable status message describing the current state (e.g., "Recovery complete", "Processing 12 coins in 3 tasks", "No recovery needed").
total_coins integer
Total number of coins found across all interrupted tasks.
orphan_coins integer
Number of coins found without a task ID. These are moved to the Trash folder automatically.
task_count integer
Number of interrupted tasks being recovered.
tasks array
Array of task objects describing each recovery task. See task object properties below.

Task Object Properties

task_id string
The original task ID from the interrupted deposit operation.
wallet string
Display name of the wallet containing the coins.
coin_count integer
Number of coins in this recovery task.
status string
Current status: "pending", "in_progress", "complete", or "failed".
phase string
Current processing phase (e.g., "POWN", "GRADE").
message string
Status message for this specific task.

Example Response - No Recovery Needed

{
  "command": "recovery-status",
  "success": true,
  "recovering": false,
  "scan_complete": true,
  "message": "No recovery needed",
  "total_coins": 0,
  "orphan_coins": 0,
  "task_count": 0,
  "tasks": []
}

Example Response - Recovery In Progress

{
  "command": "recovery-status",
  "success": true,
  "recovering": true,
  "scan_complete": true,
  "message": "Processing 15 coins in 2 tasks",
  "total_coins": 15,
  "orphan_coins": 1,
  "task_count": 2,
  "tasks": [
    {
      "task_id": "a1b2c3d4e5f6",
      "wallet": "Default",
      "coin_count": 10,
      "status": "in_progress",
      "phase": "POWN",
      "message": "Authenticating coins..."
    },
    {
      "task_id": "f6e5d4c3b2a1",
      "wallet": "Default",
      "coin_count": 5,
      "status": "pending",
      "phase": "",
      "message": "Waiting"
    }
  ]
}

Example Response - Recovery Complete

{
  "command": "recovery-status",
  "success": true,
  "recovering": false,
  "scan_complete": true,
  "message": "Recovery complete",
  "total_coins": 15,
  "orphan_coins": 1,
  "task_count": 2,
  "tasks": [
    {
      "task_id": "a1b2c3d4e5f6",
      "wallet": "Default",
      "coin_count": 10,
      "status": "complete",
      "phase": "GRADE",
      "message": "Done"
    },
    {
      "task_id": "f6e5d4c3b2a1",
      "wallet": "Default",
      "coin_count": 5,
      "status": "complete",
      "phase": "GRADE",
      "message": "Done"
    }
  ]
}

Examples

JavaScript (fetch)

const API_HOST = 'http://localhost:8080';

/**
 * Wait for startup recovery to complete before enabling the UI.
 * Call this when the application starts.
 */
async function waitForRecovery(onProgress) {
    const POLL_INTERVAL = 2000; // 2 seconds

    while (true) {
        try {
            const response = await fetch(`${API_HOST}/api/recovery/status`);
            const result = await response.json();

            if (!result.success) {
                throw new Error('Recovery status check failed');
            }

            // Report progress to the UI
            if (onProgress) {
                onProgress(result);
            }

            // Ready when scan is done and no longer recovering
            if (result.scan_complete && !result.recovering) {
                console.log('Server ready:', result.message);
                return result;
            }

            // Still working - show progress
            console.log(`Recovery: ${result.message}`);
            if (result.task_count > 0) {
                result.tasks.forEach(task => {
                    console.log(`  ${task.wallet}: ${task.coin_count} coins [${task.status}] ${task.phase}`);
                });
            }

        } catch (error) {
            console.error('Error checking recovery status:', error);
        }

        await new Promise(resolve => setTimeout(resolve, POLL_INTERVAL));
    }
}

// Usage: gate the UI on startup
async function initApp() {
    // Show loading spinner
    document.getElementById('loading').style.display = 'block';

    await waitForRecovery(status => {
        document.getElementById('loading-message').textContent = status.message;
    });

    // Hide spinner, enable UI
    document.getElementById('loading').style.display = 'none';
    document.getElementById('main-ui').style.display = 'block';
}

initApp();

cURL

# Check recovery status
curl -X GET "http://localhost:8080/api/recovery/status"

# Check if ready (recovering=false and scan_complete=true)
curl -s "http://localhost:8080/api/recovery/status" | jq '{ready: (.scan_complete and (.recovering | not)), message: .message}'

# Poll until ready (bash loop)
while true; do
    STATUS=$(curl -s "http://localhost:8080/api/recovery/status")
    RECOVERING=$(echo "$STATUS" | jq -r '.recovering')
    SCAN_DONE=$(echo "$STATUS" | jq -r '.scan_complete')
    MESSAGE=$(echo "$STATUS" | jq -r '.message')
    echo "$MESSAGE"
    if [ "$RECOVERING" = "false" ] && [ "$SCAN_DONE" = "true" ]; then
        echo "Server is ready."
        break
    fi
    sleep 2
done

Go

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "time"
)

const ApiHost = "http://localhost:8080"

type RecoveryTask struct {
    TaskID    string `json:"task_id"`
    Wallet    string `json:"wallet"`
    CoinCount int    `json:"coin_count"`
    Status    string `json:"status"`
    Phase     string `json:"phase"`
    Message   string `json:"message"`
}

type RecoveryResponse struct {
    Command      string         `json:"command"`
    Success      bool           `json:"success"`
    Recovering   bool           `json:"recovering"`
    ScanComplete bool           `json:"scan_complete"`
    Message      string         `json:"message"`
    TotalCoins   int            `json:"total_coins"`
    OrphanCoins  int            `json:"orphan_coins"`
    TaskCount    int            `json:"task_count"`
    Tasks        []RecoveryTask `json:"tasks"`
}

func getRecoveryStatus() (*RecoveryResponse, error) {
    resp, err := http.Get(fmt.Sprintf("%s/api/recovery/status", ApiHost))
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return nil, err
    }

    var result RecoveryResponse
    if err := json.Unmarshal(body, &result); err != nil {
        return nil, err
    }

    return &result, nil
}

// WaitForReady polls recovery status until the server is ready.
func WaitForReady() error {
    for {
        status, err := getRecoveryStatus()
        if err != nil {
            return fmt.Errorf("recovery check failed: %w", err)
        }

        fmt.Printf("Recovery: %s\n", status.Message)

        if status.ScanComplete && !status.Recovering {
            fmt.Println("Server is ready.")
            return nil
        }

        for _, task := range status.Tasks {
            fmt.Printf("  %s: %d coins [%s] %s\n",
                task.Wallet, task.CoinCount, task.Status, task.Phase)
        }

        time.Sleep(2 * time.Second)
    }
}

func main() {
    if err := WaitForReady(); err != nil {
        panic(err)
    }

    fmt.Println("Application ready - proceeding with normal operations.")
}

Related Endpoints

/api/system/tasks

Poll individual task progress by task ID for deposit and other async operations.

/api/transactions/deposit

Deposit coins into a wallet. Interrupted deposits are what recovery processes on startup.

/api/program/echo

Test server connectivity and get version information.