aquaman-core 0.1.0
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.
- package/LICENSE +21 -0
- package/README.md +51 -0
- package/dist/audit/index.d.ts +5 -0
- package/dist/audit/index.d.ts.map +1 -0
- package/dist/audit/index.js +5 -0
- package/dist/audit/index.js.map +1 -0
- package/dist/audit/logger.d.ts +48 -0
- package/dist/audit/logger.d.ts.map +1 -0
- package/dist/audit/logger.js +237 -0
- package/dist/audit/logger.js.map +1 -0
- package/dist/credentials/backends/onepassword.d.ts +38 -0
- package/dist/credentials/backends/onepassword.d.ts.map +1 -0
- package/dist/credentials/backends/onepassword.js +218 -0
- package/dist/credentials/backends/onepassword.js.map +1 -0
- package/dist/credentials/backends/vault.d.ts +56 -0
- package/dist/credentials/backends/vault.d.ts.map +1 -0
- package/dist/credentials/backends/vault.js +206 -0
- package/dist/credentials/backends/vault.js.map +1 -0
- package/dist/credentials/index.d.ts +7 -0
- package/dist/credentials/index.d.ts.map +1 -0
- package/dist/credentials/index.js +7 -0
- package/dist/credentials/index.js.map +1 -0
- package/dist/credentials/store.d.ts +88 -0
- package/dist/credentials/store.d.ts.map +1 -0
- package/dist/credentials/store.js +221 -0
- package/dist/credentials/store.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +85 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +11 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/config.d.ts +18 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +115 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/hash.d.ts +27 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/utils/hash.js +348 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +6 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 tech4242
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# aquaman-core
|
|
2
|
+
|
|
3
|
+
Core credential storage, audit logging, and crypto utilities for [aquaman](https://github.com/tech4242/aquaman).
|
|
4
|
+
|
|
5
|
+
## What This Is
|
|
6
|
+
|
|
7
|
+
`aquaman-core` provides the foundational building blocks for credential isolation:
|
|
8
|
+
|
|
9
|
+
- **Credential backends** — Keychain (macOS), encrypted file (Linux/CI), 1Password, HashiCorp Vault
|
|
10
|
+
- **Audit logger** — Hash-chained (SHA-256) tamper-evident logging with WAL-based crash recovery
|
|
11
|
+
- **Crypto utilities** — Key derivation, AES-256-GCM encryption, hash chain verification
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install aquaman-core
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Credential Backends
|
|
20
|
+
|
|
21
|
+
| Backend | Platform | Use Case |
|
|
22
|
+
|---------|----------|----------|
|
|
23
|
+
| `keychain` | macOS | Local dev, personal machines |
|
|
24
|
+
| `encrypted-file` | Linux, WSL2, CI/CD | Servers without native keyring |
|
|
25
|
+
| `1password` | Any (via `op` CLI) | Team credential sharing |
|
|
26
|
+
| `vault` | Any (via HTTP API) | Enterprise secrets management |
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { createCredentialStore } from 'aquaman-core/credentials';
|
|
32
|
+
import { AuditLogger } from 'aquaman-core/audit';
|
|
33
|
+
|
|
34
|
+
// Create a credential store
|
|
35
|
+
const store = await createCredentialStore({ backend: 'keychain' });
|
|
36
|
+
await store.set('anthropic', 'api_key', 'sk-ant-...');
|
|
37
|
+
const key = await store.get('anthropic', 'api_key');
|
|
38
|
+
|
|
39
|
+
// Hash-chained audit logging
|
|
40
|
+
const logger = new AuditLogger({ logDir: '~/.aquaman/audit' });
|
|
41
|
+
await logger.log({ action: 'credential_access', service: 'anthropic' });
|
|
42
|
+
await logger.verify(); // Verify chain integrity
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Documentation
|
|
46
|
+
|
|
47
|
+
See the [main README](https://github.com/tech4242/aquaman#readme) for full documentation, architecture details, and configuration options.
|
|
48
|
+
|
|
49
|
+
## License
|
|
50
|
+
|
|
51
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/audit/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,KAAK,kBAAkB,EACvB,WAAW,EACX,iBAAiB,EAClB,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/audit/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAEL,WAAW,EACX,iBAAiB,EAClB,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hash-chained audit logger with tamper-evident storage
|
|
3
|
+
*
|
|
4
|
+
* Provides cryptographic integrity verification for audit logs.
|
|
5
|
+
* Note: Credential redaction is handled by OpenClaw's built-in redaction.
|
|
6
|
+
*/
|
|
7
|
+
import type { AuditEntry, CredentialAccess } from '../types.js';
|
|
8
|
+
export interface AuditLoggerOptions {
|
|
9
|
+
logDir: string;
|
|
10
|
+
enabled?: boolean;
|
|
11
|
+
walEnabled?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare class AuditLogger {
|
|
14
|
+
private logDir;
|
|
15
|
+
private currentLogPath;
|
|
16
|
+
private walPath;
|
|
17
|
+
private enabled;
|
|
18
|
+
private walEnabled;
|
|
19
|
+
private lastHash;
|
|
20
|
+
private entryCount;
|
|
21
|
+
private initialized;
|
|
22
|
+
constructor(options: AuditLoggerOptions);
|
|
23
|
+
initialize(): Promise<void>;
|
|
24
|
+
private recoverState;
|
|
25
|
+
private recoverFromWal;
|
|
26
|
+
logToolCall(sessionId: string, agentId: string, tool: string, params: Record<string, unknown>): Promise<AuditEntry | null>;
|
|
27
|
+
logToolResult(sessionId: string, agentId: string, toolCallId: string, result: unknown, error?: string): Promise<AuditEntry | null>;
|
|
28
|
+
logCredentialAccess(sessionId: string, agentId: string, access: CredentialAccess): Promise<AuditEntry | null>;
|
|
29
|
+
private writeEntry;
|
|
30
|
+
verifyIntegrity(): Promise<{
|
|
31
|
+
valid: boolean;
|
|
32
|
+
errors: string[];
|
|
33
|
+
}>;
|
|
34
|
+
getEntries(options?: {
|
|
35
|
+
limit?: number;
|
|
36
|
+
offset?: number;
|
|
37
|
+
type?: AuditEntry['type'];
|
|
38
|
+
sessionId?: string;
|
|
39
|
+
}): Promise<AuditEntry[]>;
|
|
40
|
+
tail(count?: number): Promise<AuditEntry[]>;
|
|
41
|
+
getStats(): {
|
|
42
|
+
entryCount: number;
|
|
43
|
+
lastHash: string;
|
|
44
|
+
};
|
|
45
|
+
rotateLog(): Promise<string>;
|
|
46
|
+
}
|
|
47
|
+
export declare function createAuditLogger(options: AuditLoggerOptions): AuditLogger;
|
|
48
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/audit/logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EACV,UAAU,EAGV,gBAAgB,EACjB,MAAM,aAAa,CAAC;AAIrB,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,UAAU,CAAU;IAC5B,OAAO,CAAC,QAAQ,CAAwB;IACxC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,WAAW,CAAkB;gBAEzB,OAAO,EAAE,kBAAkB;IAQjC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YA2BnB,YAAY;YAyBZ,cAAc;IAwBtB,WAAW,CACf,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAevB,aAAa,CACjB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,OAAO,EACf,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAcvB,mBAAmB,CACvB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;YAKf,UAAU;IAiDlB,eAAe,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IA4ChE,UAAU,CAAC,OAAO,CAAC,EAAE;QACzB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;QAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAyBnB,IAAI,CAAC,KAAK,GAAE,MAAW,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAKrD,QAAQ,IAAI;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE;IAO9C,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC;CAiBnC;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAE1E"}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hash-chained audit logger with tamper-evident storage
|
|
3
|
+
*
|
|
4
|
+
* Provides cryptographic integrity verification for audit logs.
|
|
5
|
+
* Note: Credential redaction is handled by OpenClaw's built-in redaction.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'node:fs';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import { computeChainedHash, generateId } from '../utils/hash.js';
|
|
10
|
+
import { expandPath } from '../utils/config.js';
|
|
11
|
+
const GENESIS_HASH = '0000000000000000000000000000000000000000000000000000000000000000';
|
|
12
|
+
export class AuditLogger {
|
|
13
|
+
logDir;
|
|
14
|
+
currentLogPath;
|
|
15
|
+
walPath;
|
|
16
|
+
enabled;
|
|
17
|
+
walEnabled;
|
|
18
|
+
lastHash = GENESIS_HASH;
|
|
19
|
+
entryCount = 0;
|
|
20
|
+
initialized = false;
|
|
21
|
+
constructor(options) {
|
|
22
|
+
this.logDir = expandPath(options.logDir);
|
|
23
|
+
this.currentLogPath = path.join(this.logDir, 'current.jsonl');
|
|
24
|
+
this.walPath = path.join(this.logDir, 'current.wal');
|
|
25
|
+
this.enabled = options.enabled ?? true;
|
|
26
|
+
this.walEnabled = options.walEnabled ?? true;
|
|
27
|
+
}
|
|
28
|
+
async initialize() {
|
|
29
|
+
if (this.initialized)
|
|
30
|
+
return;
|
|
31
|
+
if (!this.enabled) {
|
|
32
|
+
this.initialized = true;
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
// Ensure directories exist
|
|
36
|
+
const archiveDir = path.join(this.logDir, 'archive');
|
|
37
|
+
const integrityDir = path.join(this.logDir, 'integrity');
|
|
38
|
+
fs.mkdirSync(this.logDir, { recursive: true });
|
|
39
|
+
fs.mkdirSync(archiveDir, { recursive: true });
|
|
40
|
+
fs.mkdirSync(integrityDir, { recursive: true });
|
|
41
|
+
// Recover state from existing log
|
|
42
|
+
await this.recoverState();
|
|
43
|
+
// Recover from WAL if present
|
|
44
|
+
if (this.walEnabled) {
|
|
45
|
+
await this.recoverFromWal();
|
|
46
|
+
}
|
|
47
|
+
this.initialized = true;
|
|
48
|
+
}
|
|
49
|
+
async recoverState() {
|
|
50
|
+
if (!fs.existsSync(this.currentLogPath)) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const content = fs.readFileSync(this.currentLogPath, 'utf-8');
|
|
54
|
+
const lines = content.trim().split('\n').filter(line => line.length > 0);
|
|
55
|
+
if (lines.length === 0) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
this.entryCount = lines.length;
|
|
59
|
+
// Get the hash of the last entry
|
|
60
|
+
const lastLine = lines[lines.length - 1];
|
|
61
|
+
try {
|
|
62
|
+
const lastEntry = JSON.parse(lastLine);
|
|
63
|
+
this.lastHash = lastEntry.hash;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// If we can't parse the last line, start fresh with integrity warning
|
|
67
|
+
console.error('Warning: Could not parse last audit entry, integrity may be compromised');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async recoverFromWal() {
|
|
71
|
+
if (!fs.existsSync(this.walPath)) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const walContent = fs.readFileSync(this.walPath, 'utf-8');
|
|
75
|
+
const lines = walContent.trim().split('\n').filter(line => line.length > 0);
|
|
76
|
+
for (const line of lines) {
|
|
77
|
+
try {
|
|
78
|
+
const entry = JSON.parse(line);
|
|
79
|
+
// Write to main log
|
|
80
|
+
fs.appendFileSync(this.currentLogPath, JSON.stringify(entry) + '\n');
|
|
81
|
+
this.lastHash = entry.hash;
|
|
82
|
+
this.entryCount++;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
console.error('Warning: Could not recover WAL entry');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Clear WAL after recovery
|
|
89
|
+
fs.writeFileSync(this.walPath, '');
|
|
90
|
+
}
|
|
91
|
+
async logToolCall(sessionId, agentId, tool, params) {
|
|
92
|
+
if (!this.enabled)
|
|
93
|
+
return null;
|
|
94
|
+
const toolCall = {
|
|
95
|
+
id: generateId(),
|
|
96
|
+
sessionId,
|
|
97
|
+
agentId,
|
|
98
|
+
tool,
|
|
99
|
+
params,
|
|
100
|
+
timestamp: new Date()
|
|
101
|
+
};
|
|
102
|
+
return this.writeEntry('tool_call', sessionId, agentId, toolCall);
|
|
103
|
+
}
|
|
104
|
+
async logToolResult(sessionId, agentId, toolCallId, result, error) {
|
|
105
|
+
if (!this.enabled)
|
|
106
|
+
return null;
|
|
107
|
+
const toolResult = {
|
|
108
|
+
id: generateId(),
|
|
109
|
+
toolCallId,
|
|
110
|
+
result,
|
|
111
|
+
error,
|
|
112
|
+
timestamp: new Date()
|
|
113
|
+
};
|
|
114
|
+
return this.writeEntry('tool_result', sessionId, agentId, toolResult);
|
|
115
|
+
}
|
|
116
|
+
async logCredentialAccess(sessionId, agentId, access) {
|
|
117
|
+
if (!this.enabled)
|
|
118
|
+
return null;
|
|
119
|
+
return this.writeEntry('credential_access', sessionId, agentId, access);
|
|
120
|
+
}
|
|
121
|
+
async writeEntry(type, sessionId, agentId, data) {
|
|
122
|
+
if (!this.initialized) {
|
|
123
|
+
await this.initialize();
|
|
124
|
+
}
|
|
125
|
+
const entry = {
|
|
126
|
+
id: generateId(),
|
|
127
|
+
timestamp: new Date(),
|
|
128
|
+
type,
|
|
129
|
+
sessionId,
|
|
130
|
+
agentId,
|
|
131
|
+
data,
|
|
132
|
+
previousHash: this.lastHash,
|
|
133
|
+
hash: '' // Will be computed below
|
|
134
|
+
};
|
|
135
|
+
// Compute hash including previous hash for chain integrity
|
|
136
|
+
const entryData = JSON.stringify({
|
|
137
|
+
...entry,
|
|
138
|
+
hash: undefined
|
|
139
|
+
});
|
|
140
|
+
entry.hash = computeChainedHash(entryData, this.lastHash);
|
|
141
|
+
const line = JSON.stringify(entry) + '\n';
|
|
142
|
+
// Write to WAL first (for crash recovery)
|
|
143
|
+
if (this.walEnabled) {
|
|
144
|
+
fs.appendFileSync(this.walPath, line);
|
|
145
|
+
}
|
|
146
|
+
// Write to main log
|
|
147
|
+
fs.appendFileSync(this.currentLogPath, line);
|
|
148
|
+
// Clear WAL entry after successful write
|
|
149
|
+
if (this.walEnabled) {
|
|
150
|
+
fs.writeFileSync(this.walPath, '');
|
|
151
|
+
}
|
|
152
|
+
this.lastHash = entry.hash;
|
|
153
|
+
this.entryCount++;
|
|
154
|
+
return entry;
|
|
155
|
+
}
|
|
156
|
+
async verifyIntegrity() {
|
|
157
|
+
const errors = [];
|
|
158
|
+
if (!fs.existsSync(this.currentLogPath)) {
|
|
159
|
+
return { valid: true, errors: [] };
|
|
160
|
+
}
|
|
161
|
+
const content = fs.readFileSync(this.currentLogPath, 'utf-8');
|
|
162
|
+
const lines = content.trim().split('\n').filter(line => line.length > 0);
|
|
163
|
+
let previousHash = GENESIS_HASH;
|
|
164
|
+
for (let i = 0; i < lines.length; i++) {
|
|
165
|
+
try {
|
|
166
|
+
const entry = JSON.parse(lines[i]);
|
|
167
|
+
// Verify previous hash reference
|
|
168
|
+
if (entry.previousHash !== previousHash) {
|
|
169
|
+
errors.push(`Entry ${i}: previousHash mismatch (expected ${previousHash}, got ${entry.previousHash})`);
|
|
170
|
+
}
|
|
171
|
+
// Verify entry hash
|
|
172
|
+
const entryData = JSON.stringify({
|
|
173
|
+
...entry,
|
|
174
|
+
hash: undefined
|
|
175
|
+
});
|
|
176
|
+
const expectedHash = computeChainedHash(entryData, entry.previousHash);
|
|
177
|
+
if (entry.hash !== expectedHash) {
|
|
178
|
+
errors.push(`Entry ${i}: hash mismatch (expected ${expectedHash}, got ${entry.hash})`);
|
|
179
|
+
}
|
|
180
|
+
previousHash = entry.hash;
|
|
181
|
+
}
|
|
182
|
+
catch (parseError) {
|
|
183
|
+
errors.push(`Entry ${i}: failed to parse JSON`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
valid: errors.length === 0,
|
|
188
|
+
errors
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
async getEntries(options) {
|
|
192
|
+
if (!fs.existsSync(this.currentLogPath)) {
|
|
193
|
+
return [];
|
|
194
|
+
}
|
|
195
|
+
const content = fs.readFileSync(this.currentLogPath, 'utf-8');
|
|
196
|
+
const lines = content.trim().split('\n').filter(line => line.length > 0);
|
|
197
|
+
let entries = lines.map(line => JSON.parse(line));
|
|
198
|
+
// Apply filters
|
|
199
|
+
if (options?.type) {
|
|
200
|
+
entries = entries.filter(e => e.type === options.type);
|
|
201
|
+
}
|
|
202
|
+
if (options?.sessionId) {
|
|
203
|
+
entries = entries.filter(e => e.sessionId === options.sessionId);
|
|
204
|
+
}
|
|
205
|
+
// Apply pagination
|
|
206
|
+
const offset = options?.offset ?? 0;
|
|
207
|
+
const limit = options?.limit ?? entries.length;
|
|
208
|
+
return entries.slice(offset, offset + limit);
|
|
209
|
+
}
|
|
210
|
+
async tail(count = 10) {
|
|
211
|
+
const entries = await this.getEntries();
|
|
212
|
+
return entries.slice(-count);
|
|
213
|
+
}
|
|
214
|
+
getStats() {
|
|
215
|
+
return {
|
|
216
|
+
entryCount: this.entryCount,
|
|
217
|
+
lastHash: this.lastHash
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
async rotateLog() {
|
|
221
|
+
if (!fs.existsSync(this.currentLogPath)) {
|
|
222
|
+
throw new Error('No log file to rotate');
|
|
223
|
+
}
|
|
224
|
+
const archiveDir = path.join(this.logDir, 'archive');
|
|
225
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
226
|
+
const archivePath = path.join(archiveDir, `audit-${timestamp}.jsonl`);
|
|
227
|
+
fs.renameSync(this.currentLogPath, archivePath);
|
|
228
|
+
// Reset state for new log
|
|
229
|
+
this.lastHash = GENESIS_HASH;
|
|
230
|
+
this.entryCount = 0;
|
|
231
|
+
return archivePath;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
export function createAuditLogger(options) {
|
|
235
|
+
return new AuditLogger(options);
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/audit/logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 1Password credential backend using the `op` CLI
|
|
3
|
+
* Requires: 1Password CLI installed and signed in
|
|
4
|
+
*/
|
|
5
|
+
import type { CredentialStore } from '../store.js';
|
|
6
|
+
export interface OnePasswordStoreOptions {
|
|
7
|
+
vault?: string;
|
|
8
|
+
account?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare class OnePasswordStore implements CredentialStore {
|
|
11
|
+
private vault;
|
|
12
|
+
private account?;
|
|
13
|
+
private opPath;
|
|
14
|
+
constructor(options?: OnePasswordStoreOptions);
|
|
15
|
+
private validateOpCli;
|
|
16
|
+
private runOp;
|
|
17
|
+
private getItemName;
|
|
18
|
+
private parseItemName;
|
|
19
|
+
private ensureVaultExists;
|
|
20
|
+
get(service: string, key: string): Promise<string | null>;
|
|
21
|
+
set(service: string, key: string, value: string, metadata?: Record<string, string>): Promise<void>;
|
|
22
|
+
delete(service: string, key: string): Promise<boolean>;
|
|
23
|
+
list(service?: string): Promise<Array<{
|
|
24
|
+
service: string;
|
|
25
|
+
key: string;
|
|
26
|
+
}>>;
|
|
27
|
+
exists(service: string, key: string): Promise<boolean>;
|
|
28
|
+
/**
|
|
29
|
+
* Get the vault name being used
|
|
30
|
+
*/
|
|
31
|
+
getVault(): string;
|
|
32
|
+
/**
|
|
33
|
+
* Check if 1Password CLI is available and signed in
|
|
34
|
+
*/
|
|
35
|
+
static isAvailable(): boolean;
|
|
36
|
+
}
|
|
37
|
+
export declare function createOnePasswordStore(options?: OnePasswordStoreOptions): OnePasswordStore;
|
|
38
|
+
//# sourceMappingURL=onepassword.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onepassword.d.ts","sourceRoot":"","sources":["../../../src/credentials/backends/onepassword.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,MAAM,WAAW,uBAAuB;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAKD,qBAAa,gBAAiB,YAAW,eAAe;IACtD,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,OAAO,CAAC,CAAS;IACzB,OAAO,CAAC,MAAM,CAAuB;gBAEzB,OAAO,CAAC,EAAE,uBAAuB;IAM7C,OAAO,CAAC,aAAa;IAoBrB,OAAO,CAAC,KAAK;IAyBb,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,aAAa;IAcrB,OAAO,CAAC,iBAAiB;IAcnB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAsBzD,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAsClG,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiBtD,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAiCxE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK5D;;OAEG;IACH,QAAQ,IAAI,MAAM;IAIlB;;OAEG;IACH,MAAM,CAAC,WAAW,IAAI,OAAO;CAa9B;AAED,wBAAgB,sBAAsB,CAAC,OAAO,CAAC,EAAE,uBAAuB,GAAG,gBAAgB,CAE1F"}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 1Password credential backend using the `op` CLI
|
|
3
|
+
* Requires: 1Password CLI installed and signed in
|
|
4
|
+
*/
|
|
5
|
+
import { spawnSync } from 'node:child_process';
|
|
6
|
+
const DEFAULT_VAULT = 'aquaman';
|
|
7
|
+
const ITEM_PREFIX = 'aquaman';
|
|
8
|
+
export class OnePasswordStore {
|
|
9
|
+
vault;
|
|
10
|
+
account;
|
|
11
|
+
opPath = null;
|
|
12
|
+
constructor(options) {
|
|
13
|
+
this.vault = options?.vault || DEFAULT_VAULT;
|
|
14
|
+
this.account = options?.account;
|
|
15
|
+
this.validateOpCli();
|
|
16
|
+
}
|
|
17
|
+
validateOpCli() {
|
|
18
|
+
// Check if op CLI is installed
|
|
19
|
+
try {
|
|
20
|
+
const result = spawnSync('which', ['op'], { encoding: 'utf-8' });
|
|
21
|
+
if (result.status !== 0) {
|
|
22
|
+
throw new Error('1Password CLI (op) not found. Install from: https://1password.com/downloads/command-line/');
|
|
23
|
+
}
|
|
24
|
+
this.opPath = result.stdout.trim();
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
throw new Error('1Password CLI (op) not found. Install from: https://1password.com/downloads/command-line/');
|
|
28
|
+
}
|
|
29
|
+
// Check if signed in
|
|
30
|
+
try {
|
|
31
|
+
this.runOp(['account', 'get']);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
throw new Error('Not signed in to 1Password. Run: op signin');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
runOp(args, input) {
|
|
38
|
+
const accountArgs = this.account ? ['--account', this.account] : [];
|
|
39
|
+
const fullArgs = [...args, ...accountArgs];
|
|
40
|
+
try {
|
|
41
|
+
const result = spawnSync('op', fullArgs, {
|
|
42
|
+
encoding: 'utf-8',
|
|
43
|
+
input,
|
|
44
|
+
maxBuffer: 10 * 1024 * 1024
|
|
45
|
+
});
|
|
46
|
+
if (result.status !== 0) {
|
|
47
|
+
const error = result.stderr || result.stdout || 'Unknown error';
|
|
48
|
+
throw new Error(`op command failed: ${error}`);
|
|
49
|
+
}
|
|
50
|
+
return result.stdout;
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (error instanceof Error && error.message.includes('op command failed')) {
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
throw new Error(`Failed to run op command: ${error}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
getItemName(service, key) {
|
|
60
|
+
return `${ITEM_PREFIX}-${service}-${key}`;
|
|
61
|
+
}
|
|
62
|
+
parseItemName(itemName) {
|
|
63
|
+
if (!itemName.startsWith(`${ITEM_PREFIX}-`)) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
const parts = itemName.slice(ITEM_PREFIX.length + 1).split('-');
|
|
67
|
+
if (parts.length < 2) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
// Handle service names with dashes by taking first part as service
|
|
71
|
+
const service = parts[0];
|
|
72
|
+
const key = parts.slice(1).join('-');
|
|
73
|
+
return { service, key };
|
|
74
|
+
}
|
|
75
|
+
ensureVaultExists() {
|
|
76
|
+
try {
|
|
77
|
+
this.runOp(['vault', 'get', this.vault]);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// Vault doesn't exist, create it
|
|
81
|
+
try {
|
|
82
|
+
this.runOp(['vault', 'create', this.vault]);
|
|
83
|
+
console.log(`Created 1Password vault: ${this.vault}`);
|
|
84
|
+
}
|
|
85
|
+
catch (createError) {
|
|
86
|
+
throw new Error(`Failed to create vault "${this.vault}": ${createError}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async get(service, key) {
|
|
91
|
+
const itemName = this.getItemName(service, key);
|
|
92
|
+
try {
|
|
93
|
+
const result = this.runOp([
|
|
94
|
+
'item', 'get', itemName,
|
|
95
|
+
'--vault', this.vault,
|
|
96
|
+
'--fields', 'credential',
|
|
97
|
+
'--format', 'json'
|
|
98
|
+
]);
|
|
99
|
+
const parsed = JSON.parse(result);
|
|
100
|
+
return parsed.value || null;
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
// Item not found is not an error
|
|
104
|
+
if (error instanceof Error && error.message.includes('not found')) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async set(service, key, value, metadata) {
|
|
111
|
+
this.ensureVaultExists();
|
|
112
|
+
const itemName = this.getItemName(service, key);
|
|
113
|
+
const tags = [ITEM_PREFIX, service];
|
|
114
|
+
// Check if item already exists
|
|
115
|
+
const existing = await this.get(service, key);
|
|
116
|
+
if (existing !== null) {
|
|
117
|
+
// Update existing item
|
|
118
|
+
this.runOp([
|
|
119
|
+
'item', 'edit', itemName,
|
|
120
|
+
'--vault', this.vault,
|
|
121
|
+
`credential=${value}`
|
|
122
|
+
]);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
// Create new item
|
|
126
|
+
const createArgs = [
|
|
127
|
+
'item', 'create',
|
|
128
|
+
'--category', 'API Credential',
|
|
129
|
+
'--vault', this.vault,
|
|
130
|
+
'--title', itemName,
|
|
131
|
+
`credential=${value}`,
|
|
132
|
+
'--tags', tags.join(',')
|
|
133
|
+
];
|
|
134
|
+
// Add metadata as fields
|
|
135
|
+
if (metadata) {
|
|
136
|
+
for (const [k, v] of Object.entries(metadata)) {
|
|
137
|
+
createArgs.push(`${k}=${v}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
this.runOp(createArgs);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
async delete(service, key) {
|
|
144
|
+
const itemName = this.getItemName(service, key);
|
|
145
|
+
try {
|
|
146
|
+
this.runOp([
|
|
147
|
+
'item', 'delete', itemName,
|
|
148
|
+
'--vault', this.vault
|
|
149
|
+
]);
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
if (error instanceof Error && error.message.includes('not found')) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
async list(service) {
|
|
160
|
+
try {
|
|
161
|
+
const listArgs = [
|
|
162
|
+
'item', 'list',
|
|
163
|
+
'--vault', this.vault,
|
|
164
|
+
'--tags', service ? `${ITEM_PREFIX},${service}` : ITEM_PREFIX,
|
|
165
|
+
'--format', 'json'
|
|
166
|
+
];
|
|
167
|
+
const result = this.runOp(listArgs);
|
|
168
|
+
const items = JSON.parse(result);
|
|
169
|
+
const credentials = [];
|
|
170
|
+
for (const item of items) {
|
|
171
|
+
const parsed = this.parseItemName(item.title);
|
|
172
|
+
if (parsed) {
|
|
173
|
+
if (!service || parsed.service === service) {
|
|
174
|
+
credentials.push(parsed);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return credentials;
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
// Vault might not exist yet
|
|
182
|
+
if (error instanceof Error && error.message.includes('not found')) {
|
|
183
|
+
return [];
|
|
184
|
+
}
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async exists(service, key) {
|
|
189
|
+
const value = await this.get(service, key);
|
|
190
|
+
return value !== null;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Get the vault name being used
|
|
194
|
+
*/
|
|
195
|
+
getVault() {
|
|
196
|
+
return this.vault;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Check if 1Password CLI is available and signed in
|
|
200
|
+
*/
|
|
201
|
+
static isAvailable() {
|
|
202
|
+
try {
|
|
203
|
+
const whichResult = spawnSync('which', ['op'], { encoding: 'utf-8' });
|
|
204
|
+
if (whichResult.status !== 0) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
const accountResult = spawnSync('op', ['account', 'get'], { encoding: 'utf-8' });
|
|
208
|
+
return accountResult.status === 0;
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
export function createOnePasswordStore(options) {
|
|
216
|
+
return new OnePasswordStore(options);
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=onepassword.js.map
|