Initial commit: Elydora Open Source — self-hosted audit platform for AI agents
Full self-hosted port of the Elydora tamper-evident audit protocol: - Hono API server with PostgreSQL, Redis, MinIO adapters - Ed25519 signed operations, chain hashing, Merkle epoch rollups - PBKDF2 password hashing, HMAC-SHA256 JWT, 5-role RBAC - Next.js management console - Node.js, Python, and Go SDKs - Docker Compose for local deployment - Helm chart for Kubernetes - Comprehensive security hardening and audit fixes applied Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
15547a8c35
commit
e390e68594
207 files changed
+32790
-63
No files matched your search
@@ -0,0 +1,181 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// OpenCodePlugin manages the Elydora audit hook for OpenCode.
|
||||
// It writes .opencode/plugins/elydora-audit.js in the home directory.
|
||||
type OpenCodePlugin struct{}
|
||||
|
||||
func (p *OpenCodePlugin) configPath() (string, error) {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("resolve home directory: %w", err)
|
||||
}
|
||||
return filepath.Join(home, ".opencode", "plugins", "elydora-audit.js"), nil
|
||||
}
|
||||
|
||||
func (p *OpenCodePlugin) Install(config InstallConfig) error {
|
||||
scriptPath, err := hookScriptPath(config.AgentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if config.HookScript != "" {
|
||||
scriptPath = config.HookScript
|
||||
}
|
||||
|
||||
if err := GenerateHookScript(scriptPath, config); err != nil {
|
||||
return fmt.Errorf("generate hook script: %w", err)
|
||||
}
|
||||
|
||||
guardPath := config.GuardScriptPath
|
||||
if guardPath == "" {
|
||||
guardPath, err = guardScriptPath(config.AgentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
pluginFile, err := p.configPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dir := filepath.Dir(pluginFile)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return fmt.Errorf("create directory %s: %w", dir, err)
|
||||
}
|
||||
|
||||
content := buildOpenCodePlugin(scriptPath, guardPath)
|
||||
if err := os.WriteFile(pluginFile, []byte(content), 0644); err != nil {
|
||||
return fmt.Errorf("write %s: %w", pluginFile, err)
|
||||
}
|
||||
|
||||
fmt.Printf("Installed Elydora hook for OpenCode at %s\n", pluginFile)
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildOpenCodePlugin(scriptPath, guardPath string) string {
|
||||
return fmt.Sprintf(`// Elydora Audit Plugin for OpenCode
|
||||
// Generated by @elydora/sdk — DO NOT EDIT
|
||||
|
||||
const { spawn, spawnSync } = require('node:child_process');
|
||||
|
||||
module.exports = {
|
||||
name: 'elydora-audit',
|
||||
version: '1.0.0',
|
||||
description: 'Sends tool-use events to the Elydora tamper-evident audit platform',
|
||||
|
||||
hooks: {
|
||||
preToolUse(context) {
|
||||
// Synchronous guard — blocks tool if agent is frozen
|
||||
try {
|
||||
const result = spawnSync('node', [%q], {
|
||||
timeout: 5000,
|
||||
stdio: ['pipe', 'ignore', 'pipe'],
|
||||
});
|
||||
if (result.status !== 0) {
|
||||
const msg = result.stderr ? result.stderr.toString().trim() : 'Agent is frozen by Elydora.';
|
||||
return { blocked: true, reason: msg };
|
||||
}
|
||||
} catch {
|
||||
// Fail-open — allow if guard can't run
|
||||
}
|
||||
},
|
||||
|
||||
postToolUse(context) {
|
||||
try {
|
||||
const input = JSON.stringify({
|
||||
tool_name: context.toolName || context.tool_name || 'unknown',
|
||||
tool_input: context.toolInput || context.tool_input || {},
|
||||
session_id: context.sessionId || context.session_id || 'unknown',
|
||||
});
|
||||
|
||||
const child = spawn('node', [%q], {
|
||||
detached: true,
|
||||
stdio: ['pipe', 'ignore', 'ignore'],
|
||||
});
|
||||
child.stdin.write(input);
|
||||
child.stdin.end();
|
||||
child.unref();
|
||||
} catch {
|
||||
// Fire-and-forget — never block the developer
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
`, guardPath, scriptPath)
|
||||
}
|
||||
|
||||
func (p *OpenCodePlugin) Uninstall(agentID string) error {
|
||||
pluginFile, err := p.configPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Remove(pluginFile); err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("remove %s: %w", pluginFile, err)
|
||||
}
|
||||
|
||||
if agentID != "" {
|
||||
scriptPath, _ := hookScriptPath(agentID)
|
||||
if scriptPath != "" {
|
||||
os.Remove(scriptPath)
|
||||
}
|
||||
gPath, _ := guardScriptPath(agentID)
|
||||
if gPath != "" {
|
||||
os.Remove(gPath)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("Uninstalled Elydora hook for OpenCode.")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *OpenCodePlugin) Status() (PluginStatus, error) {
|
||||
pluginFile, err := p.configPath()
|
||||
if err != nil {
|
||||
return PluginStatus{}, err
|
||||
}
|
||||
|
||||
status := PluginStatus{
|
||||
AgentName: "opencode",
|
||||
DisplayName: "OpenCode",
|
||||
ConfigPath: pluginFile,
|
||||
}
|
||||
|
||||
if _, err := os.Stat(pluginFile); err == nil {
|
||||
status.HookConfigured = true
|
||||
|
||||
// Read the plugin file to extract the hook script path
|
||||
data, readErr := os.ReadFile(pluginFile)
|
||||
if readErr == nil {
|
||||
content := string(data)
|
||||
// Look for the elydora hook.js path in the plugin file content
|
||||
if idx := strings.Index(content, ".elydora"); idx >= 0 {
|
||||
// Extract path from the spawn('node', ['/path/to/hook.js']) pattern
|
||||
// Pragmatic: scan ~/.elydora/*/hook.js for existence
|
||||
home, homeErr := os.UserHomeDir()
|
||||
if homeErr == nil {
|
||||
entries, dirErr := os.ReadDir(filepath.Join(home, ".elydora"))
|
||||
if dirErr == nil {
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
hookPath := filepath.Join(home, ".elydora", entry.Name(), "hook.js")
|
||||
if _, statErr := os.Stat(hookPath); statErr == nil {
|
||||
status.HookScriptExists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
status.Installed = status.HookConfigured && status.HookScriptExists
|
||||
return status, nil
|
||||
}
|
||||
Reference in new issue
Block a user