GridLock / GPMS monorepo: server, client, dashboard, and a shared Solana settlement package.
The repository now includes a real DCP integration track in hybrid mode:
- Transport selection: migrations can run with
ssh(default) ordcptransport mode. - Runtime setting:
settings.migration_transportcontrols default transport (sshordcp). - Fallback behavior: when transport is
dcp, server can fallback to SSH transfer if DCP orchestration fails (DCP_FALLBACK_TO_SSH=true). - DCP metadata: migrations persist
transport_kind,dcp_job_id,dcp_scheduler_url,dcp_status,dcp_error, anddcp_result_json. - Work package scaffold:
dcp-work/checkpoint.jsprovides the checkpoint orchestration work-function contract.
Current scope of DCP integration is control-plane orchestration while preserving the proven CRIU/Xpra restore path for reliability.
Add these in server/.env as needed:
MIGRATION_TRANSPORT=ssh|dcpDCP_FALLBACK_TO_SSH=true|falseDCP_SCHEDULER_URL=https://scheduler.distributed.computerDCP_ID_KEYSTORE=DCP_ACCOUNT_KEYSTORE=DCP_WORK_SCRIPT=../dcp-work/checkpoint.js
- REST:
POST /api/sessions/:id/migratewith optional body{ "targetMachineId": "machine-b", "transportKind": "dcp" } - Socket:
session:migratepayload supportstransportKind: "dcp"
On-chain payment is optional. If the server has no SOLANA_TREASURY, migrations complete without charging.
solana/— Local npm package (gridlock-solana) with pricing, wallet helpers, transfer verification, and/api/solanaroute factory. Server and client depend on it via"file:../solana"in theirpackage.json.- Server — After a successful migration, computes
payment_lamports, storespayment_status(none/pending/confirmed), and broadcastssolana:payment-request(default namespace and/clientfor the daemon). - Client daemon — If
GRIDLOCK_WALLET_PRIVATE_KEY(orGRIDLOCK_WALLET_KEYPAIRfile) is set, it signs and submits the transfer, thenPOST /api/solana/confirm. OptionalGRIDLOCK_WALLET_ADDRESSmust match the key.
From repo root, install the Solana package first (or rely on npm install in server/ and client/, which link it):
cd solana && npm install
cd ../server && npm install
cd ../client && npm installCopy server/.env.example to server/.env and set as needed:
| Variable | Purpose |
|---|---|
SOLANA_RPC_URL |
RPC (default devnet public RPC). |
SOLANA_CLUSTER |
e.g. devnet (for explorer links). |
SOLANA_TREASURY |
Base58 pubkey that receives compute payments. Empty = no settlement. |
SOLANA_BASE_LAMPORTS |
Fixed component of the fee. |
SOLANA_LAMPORTS_PER_SECOND |
Variable component × migration total_seconds. |
Optional DB settings solana_base_lamports / solana_lamports_per_second override env if set via settings API.
Copy client/.env.example to client/.env:
| Variable | Purpose |
|---|---|
GRIDLOCK_WALLET_PRIVATE_KEY |
Payer key: base58, hex, or JSON array string. If set, used instead of a keypair file. |
GRIDLOCK_WALLET_ADDRESS |
Optional; must match pubkey derived from the private key. |
GRIDLOCK_WALLET_KEYPAIR |
Path to payer keypair JSON (when GRIDLOCK_WALLET_PRIVATE_KEY is empty). Omit all wallet vars to skip auto-pay. |
SOLANA_RPC_URL |
Optional; defaults to the RPC URL sent in the socket payload / devnet. |
Fund the payer on devnet (faucet) before testing real transfers.
npm run history— Lists migrations and payment fields fromGET /api/migrations.npm run pay -- <migrationId>— Pays a single pending migration (mig-…).npm run pay-pending— Pays every migration returned byGET /api/solana/pending.
Migration history includes a Solana tx link when payment_signature is present (explorer URL uses solanaCluster from GET /api/solana/config).
- This is a direct transfer to the treasury for demos, not a custom on-chain escrow program.
POST /api/solana/confirmchecks the chain: the signature must contain a matchingsystemtransfer toSOLANA_TREASURYfor the recordedpayment_lamports.
Stream dirty pages directly from Machine A's CRIU into a criu page-server on
Machine B — no intermediate storage, no rsync of pages, no temp files. Only the
small per-snapshot metadata images go over a separate HTTP upload to the worker.
A: criu pre-dump --page-server --address B --port 1234 (round 0, 1, …)
A: criu dump --page-server --address B --port 1234 (final, only delta)
B: criu page-server -D <snap> --port 1234 (per snapshot)
B: criu restore -D <final-snap>
A->B: xpra reattach to the new host
On Machine B (target):
cd worker && npm install
WORKER_HOST=0.0.0.0 WORKER_PORT=3400 \
CHECKPOINT_BASE_DIR=/var/lib/gpms-checkpoints \
npm startOn Machine A (source):
export MACHINE_B_HOST=10.0.0.42 # hardcoded for the demo
export MACHINE_B_USER=jac
export WORKER_URL=http://${MACHINE_B_HOST}:3400
export PAGE_PORT=1234
export ITERATIONS=3 # 2 pre-dumps + 1 final dump
export CHILD_CMD='xterm -e "while true; do date; sleep 1; done"'
./gpms-precopy-demo.sh up # start xpra :110 + child app
./gpms-precopy-demo.sh tunnel # ssh -L 1234:127.0.0.1:1234 to B
./gpms-precopy-demo.sh migrate # pre-copy → final dump → restore on B
./gpms-precopy-demo.sh attach # xpra attach tcp://B:14600/If the page-server flakes during the demo, fall back to a local dump + tar upload + remote restore:
./gpms-precopy-demo.sh migrate-fallbackThe migration is also a first-class client command:
sudo node client/index.js migrate-live \
--pid 12345 \
--worker http://machine-b:3400 \
--page-host 127.0.0.1 --page-port 1234 \
--iterations 3 \
--migration-id mig-demo-1| Method | Path | Purpose |
|---|---|---|
POST |
/api/worker/migration/prepare |
Create snapshot dir, link parent to previous snapshot |
POST |
/api/worker/migration/page-server/start |
Spawn criu page-server -D <snap> --port |
POST |
/api/worker/migration/page-server/wait |
Wait for page-server to exit (after dump finishes) |
POST |
/api/worker/migration/page-server/stop |
Force-kill page-server (cleanup) |
GET |
/api/worker/migration/page-server/list |
Running page-servers |
PUT |
/api/worker/migration/file?migrationId=&snapshotIndex=&name= |
Upload one metadata image into snapshot dir |
POST |
/api/worker/migration/restore |
criu restore from the final snapshot dir |
POST |
/api/worker/migration/extract |
Fallback: extract uploaded dump.tar |