docs: Ledger Live integration, contract deploy learnings, NEXT_STEPS updates
Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled

- ADD_CHAIN138_TO_LEDGER_LIVE: Ledger form done; public code review repo bis-innovations/LedgerLive; init/push commands
- CONTRACT_DEPLOYMENT_RUNBOOK: Chain 138 gas price 1 gwei, 36-addr check, TransactionMirror workaround
- CONTRACT_*: AddressMapper, MirrorManager deployed 2026-02-12; 36-address on-chain check
- NEXT_STEPS_FOR_YOU: Ledger done; steps completable now (no LAN); run-completable-tasks-from-anywhere
- MASTER_INDEX, OPERATOR_OPTIONAL, SMART_CONTRACTS_INVENTORY_SIMPLE: updates
- LEDGER_BLOCKCHAIN_INTEGRATION_COMPLETE: bis-innovations/LedgerLive reference

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
defiQUG
2026-02-12 15:46:57 -08:00
parent cc8dcaf356
commit fbda1b4beb
5114 changed files with 498901 additions and 4567 deletions

View File

@@ -0,0 +1,17 @@
# =============================================================================
# Phoenix Deploy API — Environment (example)
# =============================================================================
# Copy to .env and fill in. Do not commit .env.
# See README.md for Gitea webhook and deploy endpoint usage.
# =============================================================================
# Listen port
PORT=4001
# Gitea instance (for commit status API)
GITEA_URL=https://gitea.d-bis.org
# Token with repo (or repo:status) scope — create at Gitea → Settings → Applications
GITEA_TOKEN=your-gitea-token
# Optional: shared secret for webhook signature and /api/deploy Bearer auth
# PHOENIX_DEPLOY_SECRET=your-webhook-and-deploy-secret

View File

@@ -0,0 +1,6 @@
# Copy to .env and set real values. .env is gitignored.
PORT=4001
GITEA_URL=https://gitea.d-bis.org
GITEA_TOKEN=
# PHOENIX_DEPLOY_SECRET=

7
phoenix-deploy-api/.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
node_modules/
.env
.env.local
.env.*.local
*.log
logs/
.DS_Store

View File

@@ -0,0 +1,40 @@
# Deploy Phoenix Deploy API
## Local run (no install)
From this directory:
```bash
npm install
cp .env.example .env # then set GITEA_TOKEN
npm start
```
## Install as systemd service (production)
1. **From this repo** (dependencies already installed in repo):
```bash
sudo ./scripts/install-systemd.sh
```
Or from repo root:
```bash
sudo phoenix-deploy-api/scripts/install-systemd.sh
```
2. **Edit env** on the server:
```bash
sudo nano /opt/phoenix-deploy-api/.env
# Set GITEA_TOKEN=... and optionally PHOENIX_DEPLOY_SECRET
sudo systemctl restart phoenix-deploy-api
```
3. **Check**: `curl http://localhost:4001/health`
## If you don't have sudo
- Run in project dir: `npm install && npm start` (or use PM2/screen).
- To install to `/opt` and systemd, run `install-systemd.sh` on a host where you have sudo.

View File

@@ -0,0 +1,51 @@
# Phoenix Deploy API
Gitea webhook receiver and deploy endpoint stub for Gitea → Phoenix deployment integration.
## Endpoints
| Method | Path | Description |
|--------|------|-------------|
| POST | /webhook/gitea | Receives Gitea push/tag/PR webhooks |
| POST | /api/deploy | Deploy request (repo, branch, target) |
| GET | /health | Health check |
## Environment
Copy `.env.example` to `.env` and set `GITEA_TOKEN` (and optionally `PHOENIX_DEPLOY_SECRET`).
| Variable | Default | Description |
|----------|---------|-------------|
| PORT | 4001 | Listen port |
| GITEA_URL | https://gitea.d-bis.org | Gitea instance URL |
| GITEA_TOKEN | | Token for commit status API |
| PHOENIX_DEPLOY_SECRET | | Optional secret for webhook/deploy auth |
## Gitea Webhook Configuration
In Gitea: Repository → Settings → Webhooks → Add Webhook
- **URL:** `https://phoenix-api-host/api/webhook/gitea` (or your Phoenix API URL)
- **Content type:** application/json
- **Events:** Push events, Tag creation (and optionally Pull requests)
- **Secret:** Optional, set PHOENIX_DEPLOY_SECRET to match
## Deploy API (Trigger from Gitea Actions)
```bash
curl -X POST "https://phoenix-api-host/api/deploy" \
-H "Authorization: Bearer $PHOENIX_DEPLOY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"repo":"d-bis/proxmox","branch":"main","sha":"abc123","target":"default"}'
```
## Integration with Sankofa Phoenix
This service is a standalone stub. Full deployment logic should be implemented in the Sankofa Phoenix API (VMID 8600). Migrate the webhook handler and deploy logic into the Phoenix API when ready.
## Run
```bash
npm install
GITEA_TOKEN=xxx npm start
```

View File

@@ -0,0 +1,14 @@
{
"name": "phoenix-deploy-api",
"version": "1.0.0",
"description": "Phoenix deploy API stub and Gitea webhook receiver for Gitea→Phoenix deployment integration",
"type": "module",
"main": "server.js",
"scripts": {
"start": "node server.js",
"dev": "node --watch server.js"
},
"dependencies": {
"express": "^4.21.0"
}
}

View File

@@ -0,0 +1,17 @@
[Unit]
Description=Phoenix Deploy API - Gitea webhook receiver
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/phoenix-deploy-api
ExecStart=/bin/node server.js
Restart=on-failure
RestartSec=5
Environment=PORT=4001
Environment=GITEA_URL=https://gitea.d-bis.org
EnvironmentFile=-/opt/phoenix-deploy-api/.env
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,29 @@
#!/usr/bin/env bash
# Install Phoenix Deploy API to /opt/phoenix-deploy-api and enable systemd service.
# Run with: sudo ./scripts/install-systemd.sh
# Or from repo root: sudo phoenix-deploy-api/scripts/install-systemd.sh
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
APP_DIR="$SCRIPT_DIR/.."
TARGET="/opt/phoenix-deploy-api"
if [ "$(id -u)" -ne 0 ]; then
echo "Run with sudo to install to $TARGET and install systemd unit."
exit 1
fi
echo "Installing Phoenix Deploy API to $TARGET ..."
mkdir -p "$TARGET"
cp -a "$APP_DIR/server.js" "$APP_DIR/package.json" "$APP_DIR/package-lock.json" "$TARGET/" 2>/dev/null || cp -a "$APP_DIR/server.js" "$APP_DIR/package.json" "$TARGET/"
[ -f "$APP_DIR/.env" ] && cp "$APP_DIR/.env" "$TARGET/.env" || [ -f "$APP_DIR/.env.example" ] && cp "$APP_DIR/.env.example" "$TARGET/.env" || true
chown -R root:root "$TARGET"
cd "$TARGET" && npm install --omit=dev
cp "$APP_DIR/phoenix-deploy-api.service" /etc/systemd/system/
systemctl daemon-reload
systemctl enable phoenix-deploy-api
systemctl start phoenix-deploy-api
echo "Done. Status: $(systemctl is-active phoenix-deploy-api)"
echo "Edit $TARGET/.env (GITEA_TOKEN, etc.) and run: systemctl restart phoenix-deploy-api"

View File

@@ -0,0 +1,154 @@
#!/usr/bin/env node
/**
* Phoenix Deploy API — Gitea webhook receiver and deploy endpoint stub
*
* Endpoints:
* POST /webhook/gitea — Receives Gitea push/tag/PR webhooks
* POST /api/deploy — Deploy request (repo, branch, target)
*
* Env: PORT, GITEA_URL, GITEA_TOKEN, PHOENIX_DEPLOY_SECRET
*/
import crypto from 'crypto';
import express from 'express';
const PORT = parseInt(process.env.PORT || '4001', 10);
const GITEA_URL = (process.env.GITEA_URL || 'https://gitea.d-bis.org').replace(/\/$/, '');
const GITEA_TOKEN = process.env.GITEA_TOKEN || '';
const WEBHOOK_SECRET = process.env.PHOENIX_DEPLOY_SECRET || '';
const app = express();
// Keep raw body for webhook HMAC verification (Gitea uses HMAC-SHA256 of body)
app.use(express.json({ verify: (req, _res, buf) => { req.rawBody = buf; } }));
/**
* Update Gitea commit status (pending/success/failure)
*/
async function setGiteaCommitStatus(owner, repo, sha, state, description, targetUrl = '') {
if (!GITEA_TOKEN) return;
const url = `${GITEA_URL}/api/v1/repos/${owner}/${repo}/statuses/${sha}`;
const body = { state, description, context: 'phoenix-deploy', target_url: targetUrl || undefined };
const res = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `token ${GITEA_TOKEN}`,
},
body: JSON.stringify(body),
});
if (!res.ok) {
console.error(`Gitea status failed: ${res.status} ${await res.text()}`);
}
}
/**
* POST /webhook/gitea — Gitea webhook receiver
* Supports: push, tag, pull_request
*/
app.post('/webhook/gitea', async (req, res) => {
const payload = req.body;
if (!payload) {
return res.status(400).json({ error: 'No payload' });
}
// Validate X-Gitea-Signature or X-Gogs-Signature (HMAC-SHA256 of raw body, hex)
if (WEBHOOK_SECRET) {
const sig = req.headers['x-gitea-signature'] || req.headers['x-gogs-signature'];
if (!sig) {
return res.status(401).json({ error: 'Missing webhook signature' });
}
const raw = req.rawBody || Buffer.from(JSON.stringify(payload));
const expected = crypto.createHmac('sha256', WEBHOOK_SECRET).update(raw).digest('hex');
const sigNormalized = String(sig).replace(/^sha256=/, '').trim();
const expectedBuf = Buffer.from(expected, 'hex');
const sigBuf = Buffer.from(sigNormalized, 'hex');
if (expectedBuf.length !== sigBuf.length || !crypto.timingSafeEqual(expectedBuf, sigBuf)) {
return res.status(401).json({ error: 'Invalid webhook signature' });
}
}
const action = payload.action || (payload.ref ? 'push' : null);
const ref = payload.ref || '';
const repo = payload.repository;
if (!repo) {
return res.status(400).json({ error: 'No repository in payload' });
}
const ownerObj = repo.owner || {};
const fullName = repo.full_name || `${ownerObj.username || ownerObj.login || 'unknown'}/${repo.name || 'repo'}`;
const [owner, repoName] = fullName.split('/');
const branch = ref.replace('refs/heads/', '').replace('refs/tags/', '');
const pr = payload.pull_request || {};
const head = pr.head || {};
const sha = payload.after || (payload.sender && payload.sender.sha) || head.sha || '';
console.log(`[webhook] ${action || 'push'} ${fullName} ${branch} ${sha}`);
if (action === 'push' || (action === 'synchronize' && payload.pull_request)) {
if (branch === 'main' || branch === 'master' || ref.startsWith('refs/tags/')) {
if (sha && GITEA_TOKEN) {
await setGiteaCommitStatus(owner, repoName, sha, 'pending', 'Phoenix deployment triggered');
}
// Stub: enqueue deploy; actual implementation would call Proxmox/deploy logic
console.log(`[deploy-stub] Would deploy ${fullName} branch=${branch} sha=${sha}`);
// Stub: when full deploy runs, call setGiteaCommitStatus(owner, repoName, sha, 'success'|'failure', ...)
}
}
res.status(200).json({ received: true, repo: fullName, branch, sha });
});
/**
* POST /api/deploy — Deploy endpoint
* Body: { repo, branch?, target?, sha? }
*/
app.post('/api/deploy', async (req, res) => {
const auth = req.headers.authorization;
if (WEBHOOK_SECRET && auth !== `Bearer ${WEBHOOK_SECRET}`) {
return res.status(401).json({ error: 'Unauthorized' });
}
const { repo, branch = 'main', target, sha } = req.body;
if (!repo) {
return res.status(400).json({ error: 'repo required' });
}
const [owner, repoName] = repo.includes('/') ? repo.split('/') : ['d-bis', repo];
const commitSha = sha || '';
if (commitSha && GITEA_TOKEN) {
await setGiteaCommitStatus(owner, repoName, commitSha, 'pending', 'Phoenix deployment in progress');
}
console.log(`[deploy] ${repo} branch=${branch} target=${target || 'default'} sha=${commitSha}`);
// Stub: no real deploy yet — report success so Gitea shows green; replace with real deploy + setGiteaCommitStatus on completion
const deploySuccess = true;
if (commitSha && GITEA_TOKEN) {
await setGiteaCommitStatus(
owner,
repoName,
commitSha,
deploySuccess ? 'success' : 'failure',
deploySuccess ? 'Deploy accepted (stub)' : 'Deploy failed (stub)'
);
}
res.status(202).json({
status: 'accepted',
repo,
branch,
target: target || 'default',
message: 'Deploy request queued (stub). Implement full deploy logic in Sankofa Phoenix API.',
});
});
/**
* GET /health — Health check
*/
app.get('/health', (req, res) => {
res.json({ status: 'ok', service: 'phoenix-deploy-api' });
});
app.listen(PORT, () => {
console.log(`Phoenix Deploy API listening on port ${PORT}`);
if (!GITEA_TOKEN) console.warn('GITEA_TOKEN not set — commit status updates disabled');
});