Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
POST /api/v1/recon/scan probes fleet ports from the server host, crawls owned HTTP targets, maps findings to spread lanes, and records optional oath ledger rows.
174 lines
4.5 KiB
Go
174 lines
4.5 KiB
Go
package db
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"time"
|
|
)
|
|
|
|
// Oath action types — immutable accountability rows.
|
|
const (
|
|
OathSpreadTierEscalation = "spread_tier_escalation"
|
|
OathGraft = "graft"
|
|
OathForkMerge = "fork_merge"
|
|
OathStrainCardPlay = "strain_card_play"
|
|
OathCourtL4Decision = "court_l4_decision"
|
|
OathSpreadAttempt = "spread_attempt"
|
|
OathStrainHospice = "strain_hospice"
|
|
OathReconScan = "recon_scan"
|
|
)
|
|
|
|
// Oath outcomes.
|
|
const (
|
|
OathOutcomeSuccess = "success"
|
|
OathOutcomeFail = "fail"
|
|
OathOutcomePending = "pending"
|
|
)
|
|
|
|
// OathLedgerEntry is one immutable operator / AI council accountability row.
|
|
type OathLedgerEntry struct {
|
|
ID int64 `json:"id"`
|
|
Timestamp string `json:"timestamp"`
|
|
Actor string `json:"actor"`
|
|
ActionType string `json:"action_type"`
|
|
AgentID string `json:"agent_id"`
|
|
Strain string `json:"strain"`
|
|
WhyHash string `json:"why_hash"`
|
|
Outcome string `json:"outcome"`
|
|
PayloadJSON json.RawMessage `json:"payload_json"`
|
|
}
|
|
|
|
func (d *Database) ensureOathLedgerTable() error {
|
|
_, err := d.Exec(`CREATE TABLE IF NOT EXISTS oath_ledger (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
actor TEXT NOT NULL DEFAULT '',
|
|
action_type TEXT NOT NULL,
|
|
agent_id TEXT NOT NULL DEFAULT '',
|
|
strain TEXT NOT NULL DEFAULT '',
|
|
why_hash TEXT NOT NULL DEFAULT '',
|
|
outcome TEXT NOT NULL DEFAULT 'pending',
|
|
payload_json TEXT NOT NULL DEFAULT '{}'
|
|
)`)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, _ = d.Exec(`CREATE INDEX IF NOT EXISTS idx_oath_ledger_ts ON oath_ledger(timestamp)`)
|
|
_, _ = d.Exec(`CREATE INDEX IF NOT EXISTS idx_oath_ledger_action ON oath_ledger(action_type)`)
|
|
return nil
|
|
}
|
|
|
|
// HashWhyJSON returns SHA256 hex of JSON-encoded source evidence (autopsy / court transcript).
|
|
func HashWhyJSON(source interface{}) string {
|
|
if source == nil {
|
|
return ""
|
|
}
|
|
raw, err := json.Marshal(source)
|
|
if err != nil {
|
|
raw = []byte("{}")
|
|
}
|
|
sum := sha256.Sum256(raw)
|
|
return hex.EncodeToString(sum[:])
|
|
}
|
|
|
|
func marshalOathPayload(payload interface{}) []byte {
|
|
if payload == nil {
|
|
return []byte("{}")
|
|
}
|
|
switch v := payload.(type) {
|
|
case json.RawMessage:
|
|
if len(v) == 0 {
|
|
return []byte("{}")
|
|
}
|
|
return v
|
|
case map[string]interface{}:
|
|
raw, err := json.Marshal(v)
|
|
if err != nil {
|
|
return []byte("{}")
|
|
}
|
|
return raw
|
|
default:
|
|
raw, err := json.Marshal(payload)
|
|
if err != nil {
|
|
return []byte("{}")
|
|
}
|
|
return raw
|
|
}
|
|
}
|
|
|
|
// InsertOathLedger appends one immutable accountability row.
|
|
func (d *Database) InsertOathLedger(actor, actionType, agentID, strain, whyHash, outcome string, payload interface{}) (*OathLedgerEntry, error) {
|
|
if d == nil {
|
|
return nil, nil
|
|
}
|
|
if err := d.ensureOathLedgerTable(); err != nil {
|
|
return nil, err
|
|
}
|
|
if outcome == "" {
|
|
outcome = OathOutcomePending
|
|
}
|
|
payloadJSON := marshalOathPayload(payload)
|
|
ts := time.Now().UTC()
|
|
res, err := d.Exec(
|
|
`INSERT INTO oath_ledger (timestamp, actor, action_type, agent_id, strain, why_hash, outcome, payload_json)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
ts, actor, actionType, agentID, strain, whyHash, outcome, string(payloadJSON),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
id, _ := res.LastInsertId()
|
|
return &OathLedgerEntry{
|
|
ID: id,
|
|
Timestamp: ts.Format(time.RFC3339),
|
|
Actor: actor,
|
|
ActionType: actionType,
|
|
AgentID: agentID,
|
|
Strain: strain,
|
|
WhyHash: whyHash,
|
|
Outcome: outcome,
|
|
PayloadJSON: json.RawMessage(payloadJSON),
|
|
}, nil
|
|
}
|
|
|
|
// ListOathLedger returns recent rows newest-first.
|
|
func (d *Database) ListOathLedger(limit int) ([]OathLedgerEntry, error) {
|
|
if d == nil {
|
|
return nil, nil
|
|
}
|
|
if err := d.ensureOathLedgerTable(); err != nil {
|
|
return nil, err
|
|
}
|
|
if limit <= 0 {
|
|
limit = 100
|
|
}
|
|
if limit > 500 {
|
|
limit = 500
|
|
}
|
|
rows, err := d.Query(
|
|
`SELECT id, timestamp, actor, action_type, agent_id, strain, why_hash, outcome, payload_json
|
|
FROM oath_ledger ORDER BY id DESC LIMIT ?`,
|
|
limit,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var out []OathLedgerEntry
|
|
for rows.Next() {
|
|
var e OathLedgerEntry
|
|
var ts time.Time
|
|
var payloadStr string
|
|
if err := rows.Scan(&e.ID, &ts, &e.Actor, &e.ActionType, &e.AgentID, &e.Strain, &e.WhyHash, &e.Outcome, &payloadStr); err != nil {
|
|
return nil, err
|
|
}
|
|
e.Timestamp = ts.UTC().Format(time.RFC3339)
|
|
if payloadStr != "" {
|
|
e.PayloadJSON = json.RawMessage(payloadStr)
|
|
}
|
|
out = append(out, e)
|
|
}
|
|
return out, rows.Err()
|
|
}
|