omniwire 3.1.5 → 3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,6 @@
1
+ {
2
+ "timestamp": "2026-03-29T23:41:14.883Z",
3
+ "backgroundTasks": [],
4
+ "sessionStartTimestamp": "2026-03-29T23:21:14.012Z",
5
+ "sessionId": "8ef02123-7368-447d-82e3-ee14a27328b0"
6
+ }
@@ -0,0 +1 @@
1
+ {"session_id":"8ef02123-7368-447d-82e3-ee14a27328b0","transcript_path":"C:\\Users\\Admin\\.claude\\projects\\C--Users-Admin\\8ef02123-7368-447d-82e3-ee14a27328b0.jsonl","cwd":"C:\\Users\\Admin\\omniwire","model":{"id":"claude-opus-4-6[1m]","display_name":"Opus 4.6 (1M context)"},"workspace":{"current_dir":"C:\\Users\\Admin\\omniwire","project_dir":"C:\\Users\\Admin","added_dirs":["C:/Users/Admin"]},"version":"2.1.87","output_style":{"name":"default"},"cost":{"total_cost_usd":4.372866899999998,"total_duration_ms":1369723,"total_api_duration_ms":587779,"total_lines_added":38,"total_lines_removed":24},"context_window":{"total_input_tokens":786,"total_output_tokens":21423,"context_window_size":1000000,"current_usage":{"input_tokens":1,"output_tokens":95,"cache_creation_input_tokens":261,"cache_read_input_tokens":99806},"used_percentage":10,"remaining_percentage":90},"exceeds_200k_tokens":false,"rate_limits":{"five_hour":{"used_percentage":35,"resets_at":1774828800},"seven_day":{"used_percentage":41,"resets_at":1775206800}}}
@@ -1,5 +1,5 @@
1
1
  {
2
- "lastCheck": 1774756238937,
2
+ "lastCheck": 1774827169153,
3
3
  "lastVersion": "3.0.1",
4
4
  "autoUpdateEnabled": true,
5
5
  "source": "auto",
package/README.md CHANGED
@@ -19,7 +19,7 @@
19
19
 
20
20
  **The infrastructure layer for AI agent swarms.**
21
21
 
22
- 81 MCP tools · A2A protocol · OmniMesh VPN · nftables firewall · CDP browser · cookie sync · CyberBase persistence
22
+ 82 MCP tools · A2A protocol · OmniMesh VPN · nftables firewall · CDP browser · cookie sync · 2FA TOTP · CyberBase persistence
23
23
 
24
24
  </div>
25
25
 
@@ -632,7 +632,7 @@ Create `~/.omniwire/mesh.json`:
632
632
  <details>
633
633
  <summary><b>v2.5.1 -- Universal Background Dispatch</b></summary>
634
634
 
635
- **`background: true`** auto-injected into all 81 tools via server-level wrapper. Returns task ID, poll with `omniwire_bg`. New `omniwire_bg` tool for list/poll/result.
635
+ **`background: true`** auto-injected into all 86 tools via server-level wrapper. Returns task ID, poll with `omniwire_bg`. New `omniwire_bg` tool for list/poll/result.
636
636
 
637
637
  </details>
638
638
 
@@ -671,7 +671,7 @@ Security fixes, multi-path SSH failover, CyberBase integration, VaultBridge Obsi
671
671
  ```
672
672
  omniwire/
673
673
  src/
674
- mcp/ MCP server (81 tools, 3 transports)
674
+ mcp/ MCP server (86 tools, 3 transports)
675
675
  nodes/ SSH2 pool, transfer engine, PTY, tunnels
676
676
  sync/ CyberSync + CyberBase (PostgreSQL, Obsidian, encryption)
677
677
  protocol/ Mesh config, types, path parsing
@@ -687,6 +687,8 @@ omniwire/
687
687
 
688
688
  | Version | Date | Changes |
689
689
  |---------|------|---------|
690
+ | **v3.2.1** | 2026-03-30 | New: 5 bi-directional sync tools (`omniwire_sync`, `omniwire_sync_rules`, `omniwire_sync_hooks`, `omniwire_sync_memory`, `omniwire_sync_agents`) — 86 tools total |
691
+ | **v3.2.0** | 2026-03-29 | New: `omniwire_2fa` TOTP manager — add/generate/verify/import/export 2FA codes, CyberBase + 1Password persistence, otpauth:// URI import, bulk code generation |
690
692
  | **v3.1.5** | 2026-03-29 | Fix: skip auto-audit batch entries from Obsidian vault + Canvas sync to prevent junk files |
691
693
  | **v3.1.4** | 2026-03-29 | Auto-sync CyberBase writes to Obsidian vault + Canvas mindmap, collision-avoidance grid placement, `sync-obsidian` / `sync-canvas` actions in knowledge tool |
692
694
  | **v3.1.3** | 2026-03-29 | OmniMesh WireGuard mesh manager, event bus (Webhook/WS/SSE), knowledge tool (12 actions), auto-update system, CDP rewrite (persistent Docker container, 18 actions), mesh expose/gateway, CyberBase circuit breaker + SQL hardening |
package/dist/mcp/index.js CHANGED
@@ -61,7 +61,7 @@ async function main() {
61
61
  const manifests = getManifests(os);
62
62
  const engine = new SyncEngine(syncDb, config, manager, transfer);
63
63
  registerSyncTools(server, syncDb, engine, manifests, nodeId, manager, transfer);
64
- log('CyberSync: 12 tools registered', { tools: 12, node: nodeId });
64
+ log('CyberSync: 17 tools registered', { tools: 17, node: nodeId });
65
65
  }
66
66
  catch (err) {
67
67
  log(`CyberSync init failed (continuing without sync): ${err.message}`, { error: err.message });
@@ -15,7 +15,7 @@ import { openBrowser } from '../commands/browser.js';
15
15
  import { allNodes, remoteNodes, findNode, NODE_ROLES, getDefaultNodeForTask, CONFIG } from '../protocol/config.js';
16
16
  import { parseMeshPath } from '../protocol/paths.js';
17
17
  import { genKeysCmd, parseKeys, buildWgConfig, wgConfigPath, bringUpCmd, bringDownCmd, statusCmd as meshStatusCmd, parseWgShow, addPeerCmd, removePeerCmd, installCmd, checkInstalledCmd, natTraversalPostUp, natTraversalPostDown, rotateKeyCmd, healthCheckCmd, stunDiscoverCmd, generateMeshTopology, } from '../mesh/omnimesh.js';
18
- const MAX_OUTPUT = 4000;
18
+ const MAX_OUTPUT = 16000;
19
19
  function t(ms) {
20
20
  return ms < 1000 ? `${ms}ms` : `${(ms / 1000).toFixed(1)}s`;
21
21
  }
@@ -33,9 +33,9 @@ function trim(s) {
33
33
  }
34
34
  // Detect table-like output — show header + tail
35
35
  const lines = s.split('\n');
36
- if (lines.length > 30) {
37
- const header = lines.slice(0, 2).join('\n');
38
- const lastLines = lines.slice(-5).join('\n');
36
+ if (lines.length > 200) {
37
+ const header = lines.slice(0, 5).join('\n');
38
+ const lastLines = lines.slice(-10).join('\n');
39
39
  return header + '\n...\n' + lastLines + `\n(${lines.length} lines total)`;
40
40
  }
41
41
  return s.slice(0, MAX_OUTPUT) + '\n...(truncated)';
@@ -3660,7 +3660,237 @@ echo "port-knock configured: ${ports.join(' -> ')} -> port ${target}"`;
3660
3660
  }
3661
3661
  return fail('invalid action');
3662
3662
  });
3663
- // --- Tool 53: omniwire_knowledge ---
3663
+ // --- Tool 53: omniwire_2fa ---
3664
+ // TOTP 2FA manager — stores secrets in CyberBase + 1Password, generates codes on demand
3665
+ const twoFaStore = new Map();
3666
+ function base32Decode(encoded) {
3667
+ const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
3668
+ const stripped = encoded.replace(/[\s=-]/g, '').toUpperCase();
3669
+ let bits = '';
3670
+ for (const c of stripped) {
3671
+ const idx = alphabet.indexOf(c);
3672
+ if (idx === -1)
3673
+ continue;
3674
+ bits += idx.toString(2).padStart(5, '0');
3675
+ }
3676
+ const bytes = [];
3677
+ for (let i = 0; i + 8 <= bits.length; i += 8)
3678
+ bytes.push(parseInt(bits.slice(i, i + 8), 2));
3679
+ return Buffer.from(bytes);
3680
+ }
3681
+ function generateTOTP(secret, options) {
3682
+ const period = options?.period ?? 30;
3683
+ const digits = options?.digits ?? 6;
3684
+ const algo = options?.algorithm ?? 'sha1';
3685
+ const now = options?.time ?? Math.floor(Date.now() / 1000);
3686
+ const counter = Math.floor(now / period);
3687
+ const counterBuf = Buffer.alloc(8);
3688
+ counterBuf.writeUInt32BE(Math.floor(counter / 0x100000000), 0);
3689
+ counterBuf.writeUInt32BE(counter & 0xFFFFFFFF, 4);
3690
+ const keyBuf = base32Decode(secret);
3691
+ const hmac = require('crypto').createHmac(algo, keyBuf).update(counterBuf).digest();
3692
+ const offset = hmac[hmac.length - 1] & 0x0f;
3693
+ const code = ((hmac[offset] & 0x7f) << 24 | hmac[offset + 1] << 16 | hmac[offset + 2] << 8 | hmac[offset + 3]) % (10 ** digits);
3694
+ return code.toString().padStart(digits, '0');
3695
+ }
3696
+ server.tool('omniwire_2fa', 'TOTP 2FA manager — add/generate/list/delete/verify/export 2FA secrets. Stores encrypted in CyberBase + 1Password. Generate codes for any stored service instantly.', {
3697
+ action: z.enum(['add', 'generate', 'list', 'delete', 'verify', 'export', 'import', 'bulk-generate']).describe('Action: add=store secret, generate=get current code, list=show all, delete=remove, verify=check code, export=dump all, import=parse otpauth URI, bulk-generate=codes for all'),
3698
+ name: z.string().optional().describe('Service name (e.g., "github", "discord", "aws")'),
3699
+ secret: z.string().optional().describe('Base32-encoded TOTP secret (for add) or otpauth:// URI (for import)'),
3700
+ issuer: z.string().optional().describe('Issuer name (e.g., "GitHub", "Discord")'),
3701
+ code: z.string().optional().describe('6-digit code to verify (for verify action)'),
3702
+ algorithm: z.enum(['sha1', 'sha256', 'sha512']).optional().describe('HMAC algorithm (default: sha1)'),
3703
+ digits: z.number().optional().describe('Code length (default: 6)'),
3704
+ period: z.number().optional().describe('Time step in seconds (default: 30)'),
3705
+ format: z.enum(['json', 'uri', 'table']).optional().describe('Export format (default: table)'),
3706
+ node: z.string().optional().describe('Node for 1Password sync'),
3707
+ background: z.boolean().optional().describe('Run in background'),
3708
+ }, async (args) => {
3709
+ const { action, name, secret, issuer, code: verifyCode, algorithm, digits, period, format, node: targetNode } = args;
3710
+ const nodeId = targetNode ?? 'contabo';
3711
+ // Load from CyberBase on first access if memory is empty
3712
+ if (twoFaStore.size === 0 && cbManager) {
3713
+ try {
3714
+ const r = await cbManager.exec('contabo', pgExec(`SELECT key, value FROM knowledge WHERE source_tool = 'omniwire' AND key LIKE '2fa:%' LIMIT 100`));
3715
+ if (r.code === 0 && r.stdout) {
3716
+ for (const line of r.stdout.split('\n')) {
3717
+ const match = line.match(/^\s*2fa:(\S+)\s*\|\s*(.+)/);
3718
+ if (match) {
3719
+ try {
3720
+ const parsed = JSON.parse(match[2].trim());
3721
+ const data = parsed.data ? JSON.parse(parsed.data) : parsed;
3722
+ twoFaStore.set(match[1], data);
3723
+ }
3724
+ catch { /* skip malformed */ }
3725
+ }
3726
+ }
3727
+ }
3728
+ }
3729
+ catch { /* CyberBase offline, continue with memory */ }
3730
+ }
3731
+ switch (action) {
3732
+ case 'add': {
3733
+ if (!name)
3734
+ return fail('name required');
3735
+ if (!secret)
3736
+ return fail('secret required');
3737
+ const entry = {
3738
+ secret: secret.replace(/\s/g, '').toUpperCase(),
3739
+ issuer: issuer ?? name,
3740
+ algorithm: algorithm ?? 'sha1',
3741
+ digits: digits ?? 6,
3742
+ period: period ?? 30,
3743
+ addedAt: new Date().toISOString(),
3744
+ };
3745
+ twoFaStore.set(name, entry);
3746
+ // Persist to CyberBase (secret stored as JSON, no raw secret in key)
3747
+ cb('2fa', name, JSON.stringify(entry));
3748
+ // Sync to 1Password
3749
+ if (cbManager) {
3750
+ const opCmd = `op item get "OmniWire 2FA - ${name}" --vault "CyberBase" >/dev/null 2>&1 && op item edit "OmniWire 2FA - ${name}" --vault "CyberBase" "notesPlain=${entry.secret}" 2>/dev/null || op item create --category=SecureNote --vault="CyberBase" --title="OmniWire 2FA - ${name}" "notesPlain=${entry.secret}" 2>/dev/null`;
3751
+ cbManager.exec(nodeId, opCmd).catch(() => { });
3752
+ }
3753
+ const currentCode = generateTOTP(entry.secret, { period: entry.period, digits: entry.digits, algorithm: entry.algorithm });
3754
+ return okBrief(`2FA added: ${name} (${entry.issuer})\nCurrent code: ${currentCode}\nStored in: memory + CyberBase + 1Password`);
3755
+ }
3756
+ case 'generate': {
3757
+ if (!name) {
3758
+ // Generate for all
3759
+ if (twoFaStore.size === 0)
3760
+ return fail('no 2FA secrets stored — use add first');
3761
+ const lines = ['Service | Code | Expires in'];
3762
+ const now = Math.floor(Date.now() / 1000);
3763
+ for (const [n, e] of twoFaStore) {
3764
+ const c = generateTOTP(e.secret, { period: e.period, digits: e.digits, algorithm: e.algorithm });
3765
+ const remaining = e.period - (now % e.period);
3766
+ lines.push(`${n} | ${c} | ${remaining}s`);
3767
+ }
3768
+ return okBrief(lines.join('\n'));
3769
+ }
3770
+ const entry = twoFaStore.get(name);
3771
+ if (!entry)
3772
+ return fail(`${name} not found — use list to see available`);
3773
+ const now = Math.floor(Date.now() / 1000);
3774
+ const currentCode = generateTOTP(entry.secret, { period: entry.period, digits: entry.digits, algorithm: entry.algorithm });
3775
+ const remaining = entry.period - (now % entry.period);
3776
+ return okBrief(`${name}: ${currentCode} (expires in ${remaining}s)`);
3777
+ }
3778
+ case 'bulk-generate': {
3779
+ if (twoFaStore.size === 0)
3780
+ return fail('no 2FA secrets stored');
3781
+ const lines = ['Service | Code | Expires'];
3782
+ const now = Math.floor(Date.now() / 1000);
3783
+ for (const [n, e] of twoFaStore) {
3784
+ const c = generateTOTP(e.secret, { period: e.period, digits: e.digits, algorithm: e.algorithm });
3785
+ const remaining = e.period - (now % e.period);
3786
+ lines.push(`${n} | ${c} | ${remaining}s`);
3787
+ }
3788
+ return okBrief(lines.join('\n'));
3789
+ }
3790
+ case 'list': {
3791
+ if (twoFaStore.size === 0)
3792
+ return okBrief('No 2FA secrets stored');
3793
+ const lines = ['Service | Issuer | Algorithm | Digits | Period | Added'];
3794
+ for (const [n, e] of twoFaStore) {
3795
+ lines.push(`${n} | ${e.issuer} | ${e.algorithm} | ${e.digits} | ${e.period}s | ${e.addedAt.split('T')[0]}`);
3796
+ }
3797
+ return okBrief(lines.join('\n'));
3798
+ }
3799
+ case 'delete': {
3800
+ if (!name)
3801
+ return fail('name required');
3802
+ if (!twoFaStore.has(name))
3803
+ return fail(`${name} not found`);
3804
+ twoFaStore.delete(name);
3805
+ // Remove from CyberBase
3806
+ if (cbManager) {
3807
+ const sql = `DELETE FROM knowledge WHERE source_tool = 'omniwire' AND key = '2fa:${sqlEscape(name)}'`;
3808
+ CB_QUEUE.push(sql);
3809
+ if (!cbDraining)
3810
+ drainCb();
3811
+ // Remove from 1Password
3812
+ cbManager.exec(nodeId, `op item delete "OmniWire 2FA - ${name}" --vault "CyberBase" 2>/dev/null`).catch(() => { });
3813
+ }
3814
+ return okBrief(`2FA deleted: ${name}`);
3815
+ }
3816
+ case 'verify': {
3817
+ if (!name)
3818
+ return fail('name required');
3819
+ if (!verifyCode)
3820
+ return fail('code required');
3821
+ const entry = twoFaStore.get(name);
3822
+ if (!entry)
3823
+ return fail(`${name} not found`);
3824
+ const now = Math.floor(Date.now() / 1000);
3825
+ // Check current window ± 1 step
3826
+ for (const offset of [0, -1, 1]) {
3827
+ const expected = generateTOTP(entry.secret, {
3828
+ period: entry.period, digits: entry.digits, algorithm: entry.algorithm,
3829
+ time: now + (offset * entry.period)
3830
+ });
3831
+ if (verifyCode === expected)
3832
+ return okBrief(`VALID — ${name} code matches (window: ${offset === 0 ? 'current' : offset < 0 ? 'previous' : 'next'})`);
3833
+ }
3834
+ return fail(`INVALID — ${name} code does not match`);
3835
+ }
3836
+ case 'import': {
3837
+ if (!secret)
3838
+ return fail('otpauth:// URI required');
3839
+ // Parse otpauth://totp/Issuer:Account?secret=XXX&issuer=YYY&algorithm=SHA1&digits=6&period=30
3840
+ const url = new URL(secret);
3841
+ if (url.protocol !== 'otpauth:')
3842
+ return fail('invalid URI — must start with otpauth://');
3843
+ const label = decodeURIComponent(url.pathname.replace(/^\/\/totp\//, ''));
3844
+ const parsedSecret = url.searchParams.get('secret');
3845
+ if (!parsedSecret)
3846
+ return fail('no secret in URI');
3847
+ const parsedIssuer = url.searchParams.get('issuer') ?? label.split(':')[0] ?? 'Unknown';
3848
+ const parsedAlgo = (url.searchParams.get('algorithm') ?? 'SHA1').toLowerCase();
3849
+ const parsedDigits = parseInt(url.searchParams.get('digits') ?? '6');
3850
+ const parsedPeriod = parseInt(url.searchParams.get('period') ?? '30');
3851
+ const entryName = name ?? label.replace(/[^a-zA-Z0-9_-]/g, '_').toLowerCase();
3852
+ const entry = {
3853
+ secret: parsedSecret.toUpperCase(),
3854
+ issuer: parsedIssuer,
3855
+ algorithm: parsedAlgo,
3856
+ digits: parsedDigits,
3857
+ period: parsedPeriod,
3858
+ addedAt: new Date().toISOString(),
3859
+ };
3860
+ twoFaStore.set(entryName, entry);
3861
+ cb('2fa', entryName, JSON.stringify(entry));
3862
+ const currentCode = generateTOTP(entry.secret, { period: entry.period, digits: entry.digits, algorithm: entry.algorithm });
3863
+ return okBrief(`Imported: ${entryName} (${parsedIssuer})\nCurrent code: ${currentCode}`);
3864
+ }
3865
+ case 'export': {
3866
+ if (twoFaStore.size === 0)
3867
+ return fail('no 2FA secrets stored');
3868
+ const fmt = format ?? 'table';
3869
+ if (fmt === 'json') {
3870
+ const data = {};
3871
+ for (const [n, e] of twoFaStore)
3872
+ data[n] = { ...e };
3873
+ return okBrief(JSON.stringify(data, null, 2));
3874
+ }
3875
+ if (fmt === 'uri') {
3876
+ const lines = [];
3877
+ for (const [n, e] of twoFaStore) {
3878
+ lines.push(`otpauth://totp/${encodeURIComponent(e.issuer)}:${encodeURIComponent(n)}?secret=${e.secret}&issuer=${encodeURIComponent(e.issuer)}&algorithm=${e.algorithm.toUpperCase()}&digits=${e.digits}&period=${e.period}`);
3879
+ }
3880
+ return okBrief(lines.join('\n'));
3881
+ }
3882
+ // table
3883
+ const lines = ['Service | Issuer | Secret (last 4) | Algorithm'];
3884
+ for (const [n, e] of twoFaStore) {
3885
+ lines.push(`${n} | ${e.issuer} | ...${e.secret.slice(-4)} | ${e.algorithm}`);
3886
+ }
3887
+ return okBrief(lines.join('\n'));
3888
+ }
3889
+ default:
3890
+ return fail('invalid action');
3891
+ }
3892
+ });
3893
+ // --- Tool 54: omniwire_knowledge ---
3664
3894
  server.tool('omniwire_knowledge', 'CyberBase knowledge base — CRUD, search, and health management for the unified PostgreSQL knowledge store. Auto-syncs all writes to Obsidian vault + Canvas mindmap. Supports text search, semantic/vector search, categories, bulk operations, and explicit sync-obsidian/sync-canvas actions.', {
3665
3895
  action: z.enum(['get', 'set', 'delete', 'search', 'semantic-search', 'list', 'stats', 'health', 'categories', 'bulk-set', 'export', 'vacuum', 'sync-obsidian', 'sync-canvas']).describe('Action'),
3666
3896
  category: z.string().optional().describe('Knowledge category (e.g., tools, vulns, infra, notes)'),