ragevault 0.2.3

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.
Files changed (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +350 -0
  3. package/dist/assets.d.ts +14 -0
  4. package/dist/assets.js +47 -0
  5. package/dist/assets.js.map +1 -0
  6. package/dist/audit.d.ts +35 -0
  7. package/dist/audit.js +79 -0
  8. package/dist/audit.js.map +1 -0
  9. package/dist/branding.d.ts +36 -0
  10. package/dist/branding.js +55 -0
  11. package/dist/branding.js.map +1 -0
  12. package/dist/crypto/cipher.d.ts +74 -0
  13. package/dist/crypto/cipher.js +95 -0
  14. package/dist/crypto/cipher.js.map +1 -0
  15. package/dist/crypto/cipher.test.d.ts +7 -0
  16. package/dist/crypto/cipher.test.js +50 -0
  17. package/dist/crypto/cipher.test.js.map +1 -0
  18. package/dist/generator.d.ts +36 -0
  19. package/dist/generator.js +114 -0
  20. package/dist/generator.js.map +1 -0
  21. package/dist/model.d.ts +96 -0
  22. package/dist/model.js +43 -0
  23. package/dist/model.js.map +1 -0
  24. package/dist/porters.d.ts +35 -0
  25. package/dist/porters.js +175 -0
  26. package/dist/porters.js.map +1 -0
  27. package/dist/prompt.d.ts +14 -0
  28. package/dist/prompt.js +56 -0
  29. package/dist/prompt.js.map +1 -0
  30. package/dist/ragevault.d.ts +22 -0
  31. package/dist/ragevault.js +814 -0
  32. package/dist/ragevault.js.map +1 -0
  33. package/dist/server/public/app.js +420 -0
  34. package/dist/server/public/apple-touch-icon.png +0 -0
  35. package/dist/server/public/favicon.png +0 -0
  36. package/dist/server/public/fonts/archivo.woff2 +0 -0
  37. package/dist/server/public/fonts/space-grotesk.woff2 +0 -0
  38. package/dist/server/public/icon-192.png +0 -0
  39. package/dist/server/public/icon-512.png +0 -0
  40. package/dist/server/public/icon-maskable.png +0 -0
  41. package/dist/server/public/index.html +225 -0
  42. package/dist/server/public/manifest.webmanifest +16 -0
  43. package/dist/server/public/styles.css +362 -0
  44. package/dist/server/public/sw.js +30 -0
  45. package/dist/server/server.d.ts +30 -0
  46. package/dist/server/server.js +410 -0
  47. package/dist/server/server.js.map +1 -0
  48. package/dist/server/session.d.ts +36 -0
  49. package/dist/server/session.js +89 -0
  50. package/dist/server/session.js.map +1 -0
  51. package/dist/store/vaultfile.d.ts +64 -0
  52. package/dist/store/vaultfile.js +110 -0
  53. package/dist/store/vaultfile.js.map +1 -0
  54. package/dist/store/vaultfile.test.d.ts +5 -0
  55. package/dist/store/vaultfile.test.js +45 -0
  56. package/dist/store/vaultfile.test.js.map +1 -0
  57. package/dist/totp.d.ts +26 -0
  58. package/dist/totp.js +88 -0
  59. package/dist/totp.js.map +1 -0
  60. package/dist/totp.test.d.ts +5 -0
  61. package/dist/totp.test.js +41 -0
  62. package/dist/totp.test.js.map +1 -0
  63. package/dist/tui/ansi.d.ts +34 -0
  64. package/dist/tui/ansi.js +52 -0
  65. package/dist/tui/ansi.js.map +1 -0
  66. package/dist/tui/app.d.ts +83 -0
  67. package/dist/tui/app.js +607 -0
  68. package/dist/tui/app.js.map +1 -0
  69. package/dist/update/channel.d.ts +53 -0
  70. package/dist/update/channel.js +135 -0
  71. package/dist/update/channel.js.map +1 -0
  72. package/package.json +68 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branding.js","sourceRoot":"","sources":["../src/branding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,wCAAwC;AACxC,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC;IACjC,OAAO,EAAE,WAAW;IACpB,MAAM,EAAE,gBAAgB;IACxB,GAAG,EAAE,qBAAqB;IAC1B,MAAM,EAAE,+CAA+C;IACvD,OAAO,EAAE,uDAAuD;IAChE,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC;IACnC,KAAK,EAAE,SAAS;IAChB,GAAG,EAAE,SAAS;IACd,MAAM,EAAE,SAAS;IACjB,KAAK,EAAE,SAAS;IAChB,KAAK,EAAE,SAAS;IAChB,GAAG,EAAE,SAAS;CACf,CAAC,CAAC;AAEH,gDAAgD;AAChD,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;CASrC,CAAC;AAEF,gDAAgD;AAChD,MAAM,UAAU,YAAY;IAC1B,OAAO;QACL,YAAY;QACZ,KAAK,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO,EAAE;QACtC,0BAA0B,KAAK,CAAC,MAAM,MAAM,KAAK,CAAC,GAAG,EAAE;QACvD,+CAA+C;QAC/C,0BAA0B,KAAK,CAAC,MAAM,EAAE;QACxC,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * The cryptographic core — deliberately small, standard, and auditable.
3
+ *
4
+ * RageVault never invents crypto. Everything here is a thin, well-labelled layer
5
+ * over Node's built-in primitives (no native or third-party crypto deps):
6
+ *
7
+ * • Key derivation: scrypt (memory-hard) turns the master password + a random
8
+ * per-vault salt into a 256-bit Key-Encryption-Key (KEK). Parameters live in
9
+ * the vault header so they can be raised over time.
10
+ * • Key hierarchy: a random 256-bit Data-Encryption-Key (DEK) actually
11
+ * encrypts your secrets; it is *wrapped* (encrypted) by the KEK. Changing the
12
+ * master password only re-wraps the DEK — it never re-encrypts every item.
13
+ * • Encryption: AES-256-GCM (authenticated) with a fresh random 96-bit IV
14
+ * per operation. The GCM auth tag is what makes a wrong password or a tampered
15
+ * file *fail loudly* instead of returning garbage.
16
+ *
17
+ * The server (when self-hosted) only ever stores ciphertext, the wrapped DEK and
18
+ * the KDF salt/params. Without the master password there is nothing to read.
19
+ *
20
+ * Part of RageVault.
21
+ * Crafted by SoyRage Agency — https://soyrage.es/
22
+ * MIT licensed (see LICENSE).
23
+ */
24
+ /** Tunable scrypt parameters, stored per-vault so they can be upgraded. */
25
+ export interface KdfParams {
26
+ algo: "scrypt";
27
+ /** CPU/memory cost — a power of two. 2^16 ≈ 64 MB per hash. */
28
+ N: number;
29
+ r: number;
30
+ p: number;
31
+ keyLen: number;
32
+ }
33
+ /** OWASP-aligned defaults tuned for an interactive unlock (~sub-second). */
34
+ export declare const DEFAULT_KDF: KdfParams;
35
+ /** An authenticated ciphertext blob. All fields are base64. */
36
+ export interface Sealed {
37
+ /** 96-bit random nonce. */
38
+ iv: string;
39
+ /** 128-bit GCM authentication tag. */
40
+ tag: string;
41
+ /** The ciphertext. */
42
+ ct: string;
43
+ }
44
+ /** A fresh random salt for the KDF (128-bit). */
45
+ export declare function newSalt(): Buffer;
46
+ /** A fresh random Data-Encryption-Key (256-bit). */
47
+ export declare function newKey(): Buffer;
48
+ /**
49
+ * Derive the 256-bit Key-Encryption-Key from the master password and salt.
50
+ * scrypt is memory-hard, so it resists GPU/ASIC brute-forcing far better than a
51
+ * plain hash. `maxmem` is raised to fit the chosen N.
52
+ */
53
+ export declare function deriveKek(password: string, salt: Buffer, params?: KdfParams): Buffer;
54
+ /**
55
+ * Encrypt (`seal`) a plaintext with a 256-bit key using AES-256-GCM.
56
+ * `aad` (additional authenticated data) binds the ciphertext to a context — e.g.
57
+ * an item id — so blobs can't be swapped between records undetected.
58
+ */
59
+ export declare function seal(plaintext: string | Buffer, key: Buffer, aad?: string): Sealed;
60
+ /**
61
+ * Decrypt (`open`) a sealed blob. Throws if the key is wrong, the `aad` differs,
62
+ * or the ciphertext was tampered with — GCM verifies integrity for us.
63
+ */
64
+ export declare function open(sealed: Sealed, key: Buffer, aad?: string): Buffer;
65
+ /** Convenience: open a sealed blob and return UTF-8 text. */
66
+ export declare function openText(sealed: Sealed, key: Buffer, aad?: string): string;
67
+ /** Wrap (encrypt) the DEK with the KEK. */
68
+ export declare function wrapKey(dek: Buffer, kek: Buffer): Sealed;
69
+ /** Unwrap the DEK with the KEK. Throws on a wrong master password. */
70
+ export declare function unwrapKey(wrapped: Sealed, kek: Buffer): Buffer;
71
+ /** Constant-time buffer comparison (for tokens / verifiers). */
72
+ export declare function safeEqual(a: Buffer, b: Buffer): boolean;
73
+ /** Best-effort wipe of a key buffer once we're done with it. */
74
+ export declare function wipe(buf: Buffer): void;
@@ -0,0 +1,95 @@
1
+ /**
2
+ * The cryptographic core — deliberately small, standard, and auditable.
3
+ *
4
+ * RageVault never invents crypto. Everything here is a thin, well-labelled layer
5
+ * over Node's built-in primitives (no native or third-party crypto deps):
6
+ *
7
+ * • Key derivation: scrypt (memory-hard) turns the master password + a random
8
+ * per-vault salt into a 256-bit Key-Encryption-Key (KEK). Parameters live in
9
+ * the vault header so they can be raised over time.
10
+ * • Key hierarchy: a random 256-bit Data-Encryption-Key (DEK) actually
11
+ * encrypts your secrets; it is *wrapped* (encrypted) by the KEK. Changing the
12
+ * master password only re-wraps the DEK — it never re-encrypts every item.
13
+ * • Encryption: AES-256-GCM (authenticated) with a fresh random 96-bit IV
14
+ * per operation. The GCM auth tag is what makes a wrong password or a tampered
15
+ * file *fail loudly* instead of returning garbage.
16
+ *
17
+ * The server (when self-hosted) only ever stores ciphertext, the wrapped DEK and
18
+ * the KDF salt/params. Without the master password there is nothing to read.
19
+ *
20
+ * Part of RageVault.
21
+ * Crafted by SoyRage Agency — https://soyrage.es/
22
+ * MIT licensed (see LICENSE).
23
+ */
24
+ import { createCipheriv, createDecipheriv, randomBytes, scryptSync, timingSafeEqual } from "node:crypto";
25
+ /** OWASP-aligned defaults tuned for an interactive unlock (~sub-second). */
26
+ export const DEFAULT_KDF = { algo: "scrypt", N: 1 << 16, r: 8, p: 1, keyLen: 32 };
27
+ const IV_LEN = 12; // 96-bit nonce, the GCM standard
28
+ /** A fresh random salt for the KDF (128-bit). */
29
+ export function newSalt() {
30
+ return randomBytes(16);
31
+ }
32
+ /** A fresh random Data-Encryption-Key (256-bit). */
33
+ export function newKey() {
34
+ return randomBytes(32);
35
+ }
36
+ /**
37
+ * Derive the 256-bit Key-Encryption-Key from the master password and salt.
38
+ * scrypt is memory-hard, so it resists GPU/ASIC brute-forcing far better than a
39
+ * plain hash. `maxmem` is raised to fit the chosen N.
40
+ */
41
+ export function deriveKek(password, salt, params = DEFAULT_KDF) {
42
+ const maxmem = 256 * params.N * params.r; // headroom over scrypt's 128*N*r footprint
43
+ return scryptSync(password.normalize("NFKC"), salt, params.keyLen, {
44
+ N: params.N,
45
+ r: params.r,
46
+ p: params.p,
47
+ maxmem,
48
+ });
49
+ }
50
+ /**
51
+ * Encrypt (`seal`) a plaintext with a 256-bit key using AES-256-GCM.
52
+ * `aad` (additional authenticated data) binds the ciphertext to a context — e.g.
53
+ * an item id — so blobs can't be swapped between records undetected.
54
+ */
55
+ export function seal(plaintext, key, aad) {
56
+ const iv = randomBytes(IV_LEN);
57
+ const cipher = createCipheriv("aes-256-gcm", key, iv);
58
+ if (aad)
59
+ cipher.setAAD(Buffer.from(aad, "utf8"));
60
+ const data = typeof plaintext === "string" ? Buffer.from(plaintext, "utf8") : plaintext;
61
+ const ct = Buffer.concat([cipher.update(data), cipher.final()]);
62
+ return { iv: iv.toString("base64"), tag: cipher.getAuthTag().toString("base64"), ct: ct.toString("base64") };
63
+ }
64
+ /**
65
+ * Decrypt (`open`) a sealed blob. Throws if the key is wrong, the `aad` differs,
66
+ * or the ciphertext was tampered with — GCM verifies integrity for us.
67
+ */
68
+ export function open(sealed, key, aad) {
69
+ const decipher = createDecipheriv("aes-256-gcm", key, Buffer.from(sealed.iv, "base64"));
70
+ if (aad)
71
+ decipher.setAAD(Buffer.from(aad, "utf8"));
72
+ decipher.setAuthTag(Buffer.from(sealed.tag, "base64"));
73
+ return Buffer.concat([decipher.update(Buffer.from(sealed.ct, "base64")), decipher.final()]);
74
+ }
75
+ /** Convenience: open a sealed blob and return UTF-8 text. */
76
+ export function openText(sealed, key, aad) {
77
+ return open(sealed, key, aad).toString("utf8");
78
+ }
79
+ /** Wrap (encrypt) the DEK with the KEK. */
80
+ export function wrapKey(dek, kek) {
81
+ return seal(dek, kek, "ragevault:dek");
82
+ }
83
+ /** Unwrap the DEK with the KEK. Throws on a wrong master password. */
84
+ export function unwrapKey(wrapped, kek) {
85
+ return open(wrapped, kek, "ragevault:dek");
86
+ }
87
+ /** Constant-time buffer comparison (for tokens / verifiers). */
88
+ export function safeEqual(a, b) {
89
+ return a.length === b.length && timingSafeEqual(a, b);
90
+ }
91
+ /** Best-effort wipe of a key buffer once we're done with it. */
92
+ export function wipe(buf) {
93
+ buf.fill(0);
94
+ }
95
+ //# sourceMappingURL=cipher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cipher.js","sourceRoot":"","sources":["../../src/crypto/cipher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Crypto core tests — the security guarantees, made executable.
3
+ * Run with: npm test (node --test)
4
+ *
5
+ * Part of RageVault. Crafted by SoyRage Agency — https://soyrage.es/
6
+ */
7
+ export {};
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Crypto core tests — the security guarantees, made executable.
3
+ * Run with: npm test (node --test)
4
+ *
5
+ * Part of RageVault. Crafted by SoyRage Agency — https://soyrage.es/
6
+ */
7
+ import { test } from "node:test";
8
+ import assert from "node:assert/strict";
9
+ import { deriveKek, newKey, newSalt, open, openText, seal, unwrapKey, wrapKey } from "./cipher.js";
10
+ // Fast params for tests — production uses N=2^16.
11
+ const FAST = { algo: "scrypt", N: 1 << 10, r: 8, p: 1, keyLen: 32 };
12
+ test("seal → open round-trips arbitrary data", () => {
13
+ const key = newKey();
14
+ const s = seal("hunter2 · 🔐 · ünïcode", key);
15
+ assert.equal(openText(s, key), "hunter2 · 🔐 · ünïcode");
16
+ });
17
+ test("open with the wrong key throws (GCM auth)", () => {
18
+ const s = seal("secret", newKey());
19
+ assert.throws(() => open(s, newKey()));
20
+ });
21
+ test("tampering with the ciphertext is detected", () => {
22
+ const key = newKey();
23
+ const s = seal("secret", key);
24
+ const bytes = Buffer.from(s.ct, "base64");
25
+ bytes[0] ^= 0xff;
26
+ assert.throws(() => open({ ...s, ct: bytes.toString("base64") }, key));
27
+ });
28
+ test("AAD mismatch is rejected", () => {
29
+ const key = newKey();
30
+ const s = seal("secret", key, "item:1");
31
+ assert.throws(() => open(s, key, "item:2"));
32
+ });
33
+ test("deriveKek is deterministic for the same password+salt, and salt-sensitive", () => {
34
+ const salt = newSalt();
35
+ const a = deriveKek("correct horse", salt, FAST);
36
+ const b = deriveKek("correct horse", salt, FAST);
37
+ const c = deriveKek("correct horse", newSalt(), FAST);
38
+ assert.ok(a.equals(b), "same inputs → same key");
39
+ assert.ok(!a.equals(c), "different salt → different key");
40
+ });
41
+ test("DEK wrap/unwrap round-trips; wrong password fails", () => {
42
+ const salt = newSalt();
43
+ const dek = newKey();
44
+ const kek = deriveKek("master", salt, FAST);
45
+ const wrapped = wrapKey(dek, kek);
46
+ assert.ok(unwrapKey(wrapped, kek).equals(dek), "correct KEK recovers the DEK");
47
+ const wrongKek = deriveKek("wrong", salt, FAST);
48
+ assert.throws(() => unwrapKey(wrapped, wrongKek), "wrong password cannot unwrap");
49
+ });
50
+ //# sourceMappingURL=cipher.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cipher.test.js","sourceRoot":"","sources":["../../src/crypto/cipher.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Password & passphrase generator — the "Password Pusher"-style toolbox.
3
+ *
4
+ * Uses the CSPRNG (`randomInt`) for unbiased selection, offers random-character,
5
+ * pronounceable and diceware-style passphrase modes, and ships a fast entropy
6
+ * estimator so the UI can show a real strength read (not a vibes-based meter).
7
+ *
8
+ * Part of RageVault.
9
+ * Crafted by SoyRage Agency — https://soyrage.es/
10
+ * MIT licensed (see LICENSE).
11
+ */
12
+ export interface GenOptions {
13
+ length: number;
14
+ lower: boolean;
15
+ upper: boolean;
16
+ digits: boolean;
17
+ symbols: boolean;
18
+ /** Drop visually ambiguous characters. */
19
+ avoidAmbiguous: boolean;
20
+ }
21
+ export declare const DEFAULT_GEN: GenOptions;
22
+ /** A random-character password honouring the selected sets. */
23
+ export declare function generatePassword(opts?: Partial<GenOptions>): string;
24
+ /** A pronounceable password (alternating CV syllables) with a digit + symbol. */
25
+ export declare function generatePronounceable(syllables?: number): string;
26
+ /**
27
+ * A diceware-style passphrase. A compact word list is embedded so it works
28
+ * offline / inside the standalone binary; swap in the full EFF list for max
29
+ * entropy if you ship it as an asset.
30
+ */
31
+ export declare function generatePassphrase(words?: number, separator?: string, capitalize?: boolean): string;
32
+ /** Rough entropy in bits + a human label. Not a substitute for zxcvbn, but honest. */
33
+ export declare function estimateStrength(pw: string): {
34
+ bits: number;
35
+ label: string;
36
+ };
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Password & passphrase generator — the "Password Pusher"-style toolbox.
3
+ *
4
+ * Uses the CSPRNG (`randomInt`) for unbiased selection, offers random-character,
5
+ * pronounceable and diceware-style passphrase modes, and ships a fast entropy
6
+ * estimator so the UI can show a real strength read (not a vibes-based meter).
7
+ *
8
+ * Part of RageVault.
9
+ * Crafted by SoyRage Agency — https://soyrage.es/
10
+ * MIT licensed (see LICENSE).
11
+ */
12
+ import { randomInt } from "node:crypto";
13
+ const LOWER = "abcdefghijkmnpqrstuvwxyz"; // no l, o (look-alikes)
14
+ const UPPER = "ABCDEFGHJKLMNPQRSTUVWXYZ"; // no I, O
15
+ const DIGITS = "23456789"; // no 0, 1
16
+ const SYMBOLS = "!@#$%^&*()-_=+[]{};:,.?";
17
+ export const DEFAULT_GEN = {
18
+ length: 20,
19
+ lower: true,
20
+ upper: true,
21
+ digits: true,
22
+ symbols: true,
23
+ avoidAmbiguous: true,
24
+ };
25
+ /** Pick one random element (uniform, CSPRNG-backed). */
26
+ function pick(arr) {
27
+ return arr[randomInt(arr.length)];
28
+ }
29
+ /** A random-character password honouring the selected sets. */
30
+ export function generatePassword(opts = {}) {
31
+ const o = { ...DEFAULT_GEN, ...opts };
32
+ const pools = [];
33
+ if (o.lower)
34
+ pools.push(o.avoidAmbiguous ? LOWER : "abcdefghijklmnopqrstuvwxyz");
35
+ if (o.upper)
36
+ pools.push(o.avoidAmbiguous ? UPPER : "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
37
+ if (o.digits)
38
+ pools.push(o.avoidAmbiguous ? DIGITS : "0123456789");
39
+ if (o.symbols)
40
+ pools.push(SYMBOLS);
41
+ if (pools.length === 0)
42
+ pools.push(LOWER);
43
+ const all = pools.join("");
44
+ const chars = [];
45
+ // Guarantee at least one from each selected pool, then fill the rest.
46
+ for (const pool of pools)
47
+ chars.push(pick(pool));
48
+ while (chars.length < o.length)
49
+ chars.push(pick(all));
50
+ // Fisher–Yates shuffle so the guaranteed chars aren't always at the front.
51
+ for (let i = chars.length - 1; i > 0; i--) {
52
+ const j = randomInt(i + 1);
53
+ [chars[i], chars[j]] = [chars[j], chars[i]];
54
+ }
55
+ return chars.slice(0, o.length).join("");
56
+ }
57
+ const CONSONANTS = "bcdfghjkmnprstvwz";
58
+ const VOWELS = "aeiou";
59
+ /** A pronounceable password (alternating CV syllables) with a digit + symbol. */
60
+ export function generatePronounceable(syllables = 5) {
61
+ let out = "";
62
+ for (let i = 0; i < syllables; i++) {
63
+ let c = pick(CONSONANTS);
64
+ if (i === 0)
65
+ c = c.toUpperCase();
66
+ out += c + pick(VOWELS);
67
+ }
68
+ return `${out}${pick(DIGITS)}${pick("!@#$%&*")}`;
69
+ }
70
+ /**
71
+ * A diceware-style passphrase. A compact word list is embedded so it works
72
+ * offline / inside the standalone binary; swap in the full EFF list for max
73
+ * entropy if you ship it as an asset.
74
+ */
75
+ export function generatePassphrase(words = 5, separator = "-", capitalize = true) {
76
+ const list = WORDS;
77
+ const out = [];
78
+ for (let i = 0; i < words; i++) {
79
+ let w = list[randomInt(list.length)];
80
+ if (capitalize)
81
+ w = w[0].toUpperCase() + w.slice(1);
82
+ out.push(w);
83
+ }
84
+ // Sprinkle a number so it satisfies "needs a digit" policies.
85
+ return out.join(separator) + separator + String(randomInt(10, 100));
86
+ }
87
+ /** Rough entropy in bits + a human label. Not a substitute for zxcvbn, but honest. */
88
+ export function estimateStrength(pw) {
89
+ let pool = 0;
90
+ if (/[a-z]/.test(pw))
91
+ pool += 26;
92
+ if (/[A-Z]/.test(pw))
93
+ pool += 26;
94
+ if (/[0-9]/.test(pw))
95
+ pool += 10;
96
+ if (/[^a-zA-Z0-9]/.test(pw))
97
+ pool += 30;
98
+ const bits = pw.length * (pool ? Math.log2(pool) : 0);
99
+ const label = bits < 40 ? "weak" : bits < 60 ? "fair" : bits < 80 ? "strong" : "excellent";
100
+ return { bits: Math.round(bits), label };
101
+ }
102
+ // A small, curated word list (kept short here; replace with the EFF long list
103
+ // as a bundled asset for ~12.9 bits/word). These are common, easy-to-type words.
104
+ const WORDS = [
105
+ "amber", "anchor", "apple", "arrow", "atlas", "aurora", "basil", "beacon", "birch", "bloom",
106
+ "bronze", "cabin", "canyon", "cedar", "cinder", "cobalt", "comet", "coral", "cove", "crane",
107
+ "delta", "dune", "ember", "falcon", "fern", "flint", "forest", "garnet", "glade", "granite",
108
+ "harbor", "hazel", "heron", "indigo", "ivory", "jade", "kite", "lagoon", "lantern", "linen",
109
+ "lotus", "maple", "marble", "meadow", "mint", "north", "oak", "ocean", "onyx", "opal",
110
+ "orbit", "pebble", "pine", "prairie", "quartz", "quill", "raven", "reef", "ripple", "river",
111
+ "rowan", "saffron", "sage", "sequoia", "silver", "slate", "solstice", "spruce", "storm", "summit",
112
+ "tide", "timber", "topaz", "trail", "tulip", "umber", "valley", "velvet", "willow", "zephyr",
113
+ ];
114
+ //# sourceMappingURL=generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.js","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * The RageVault data model.
3
+ *
4
+ * Everything here is what lives *inside* the encrypted payload — the server and
5
+ * the on-disk file only ever see the sealed form. The shapes are deliberately
6
+ * plain JSON so the vault is portable and easy to export.
7
+ *
8
+ * Part of RageVault.
9
+ * Crafted by SoyRage Agency — https://soyrage.es/
10
+ * MIT licensed (see LICENSE).
11
+ */
12
+ /** Role hierarchy for internal / team use. Higher roles subsume lower ones. */
13
+ export type Role = "owner" | "admin" | "member" | "viewer";
14
+ /** What each role may do — enforced by the app layer (see canDo). */
15
+ export declare const ROLE_RANK: Record<Role, number>;
16
+ export type Capability = "vault.manage" | "item.create" | "item.update" | "item.delete" | "item.reveal" | "item.read";
17
+ /** True if `role` is allowed to perform `cap`. */
18
+ export declare function canDo(role: Role, cap: Capability): boolean;
19
+ /** A person who can unlock/use the vault (RBAC at the app layer). */
20
+ export interface User {
21
+ id: string;
22
+ name: string;
23
+ role: Role;
24
+ createdAt: string;
25
+ /** Optional: a per-user unlock is on the roadmap; owner-managed for now. */
26
+ disabled?: boolean;
27
+ }
28
+ /** A top-level grouping (e.g. "Work", "Personal", "Infrastructure"). */
29
+ export interface Category {
30
+ id: string;
31
+ name: string;
32
+ /** A hint colour from the SoyRage palette or any hex. */
33
+ color?: string;
34
+ icon?: string;
35
+ }
36
+ /** A nested container within (or across) categories — a "branch" of the tree. */
37
+ export interface Folder {
38
+ id: string;
39
+ name: string;
40
+ parentId?: string;
41
+ categoryId?: string;
42
+ /** A custom glyph/emoji, or a colour hex — shown in the tree. */
43
+ icon?: string;
44
+ color?: string;
45
+ }
46
+ /** An extra, user-defined field — the flexible way to model cards, SSH keys,
47
+ * API tokens, identities, etc. Secret fields are blurred like the password. */
48
+ export interface CustomField {
49
+ label: string;
50
+ value: string;
51
+ /** Blur this value by default (card CVV, SSH private key, API token…). */
52
+ secret: boolean;
53
+ }
54
+ /** A secret record. Secret fields are plaintext only in memory after unlock. */
55
+ export interface Item {
56
+ id: string;
57
+ title: string;
58
+ categoryId?: string;
59
+ folderId?: string;
60
+ username?: string;
61
+ password?: string;
62
+ url?: string;
63
+ notes?: string;
64
+ /** TOTP secret (base32) for 2FA code generation. */
65
+ totp?: string;
66
+ /** Arbitrary extra fields (cards, SSH keys, tokens, secure notes…). */
67
+ fields?: CustomField[];
68
+ /** A custom glyph/emoji (or leave blank to derive an initial from the title). */
69
+ icon?: string;
70
+ tags: string[];
71
+ favorite: boolean;
72
+ createdAt: string;
73
+ updatedAt: string;
74
+ /** Previous passwords, newest first, for recovery/rotation audit. */
75
+ history: Array<{
76
+ password: string;
77
+ changedAt: string;
78
+ }>;
79
+ }
80
+ /** The decrypted vault contents. */
81
+ export interface VaultData {
82
+ meta: {
83
+ name: string;
84
+ createdAt: string;
85
+ };
86
+ users: User[];
87
+ categories: Category[];
88
+ folders: Folder[];
89
+ items: Item[];
90
+ /** Soft-deleted items, recoverable until the trash is emptied. */
91
+ trash: Item[];
92
+ }
93
+ /** A short, url-safe id. */
94
+ export declare function newId(): string;
95
+ /** An empty vault owned by the given user name. */
96
+ export declare function emptyVault(name: string, ownerName: string, now: string): VaultData;
package/dist/model.js ADDED
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The RageVault data model.
3
+ *
4
+ * Everything here is what lives *inside* the encrypted payload — the server and
5
+ * the on-disk file only ever see the sealed form. The shapes are deliberately
6
+ * plain JSON so the vault is portable and easy to export.
7
+ *
8
+ * Part of RageVault.
9
+ * Crafted by SoyRage Agency — https://soyrage.es/
10
+ * MIT licensed (see LICENSE).
11
+ */
12
+ import { randomBytes } from "node:crypto";
13
+ /** What each role may do — enforced by the app layer (see canDo). */
14
+ export const ROLE_RANK = { owner: 3, admin: 2, member: 1, viewer: 0 };
15
+ const CAP_MIN_RANK = {
16
+ "vault.manage": ROLE_RANK.owner,
17
+ "item.delete": ROLE_RANK.admin,
18
+ "item.create": ROLE_RANK.member,
19
+ "item.update": ROLE_RANK.member,
20
+ "item.reveal": ROLE_RANK.member, // a viewer sees metadata only, never secrets
21
+ "item.read": ROLE_RANK.viewer,
22
+ };
23
+ /** True if `role` is allowed to perform `cap`. */
24
+ export function canDo(role, cap) {
25
+ return ROLE_RANK[role] >= CAP_MIN_RANK[cap];
26
+ }
27
+ /** A short, url-safe id. */
28
+ export function newId() {
29
+ // 12 bytes of base64url — collision-resistant enough for local ids.
30
+ return randomBytes(12).toString("base64url");
31
+ }
32
+ /** An empty vault owned by the given user name. */
33
+ export function emptyVault(name, ownerName, now) {
34
+ return {
35
+ meta: { name, createdAt: now },
36
+ users: [{ id: newId(), name: ownerName, role: "owner", createdAt: now }],
37
+ categories: [],
38
+ folders: [],
39
+ items: [],
40
+ trash: [],
41
+ };
42
+ }
43
+ //# sourceMappingURL=model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAK1C,qEAAqE;AACrE,MAAM,CAAC,MAAM,SAAS,GAAyB,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;AAU5F,MAAM,YAAY,GAA+B;IAC/C,cAAc,EAAE,SAAS,CAAC,KAAK;IAC/B,aAAa,EAAE,SAAS,CAAC,KAAK;IAC9B,aAAa,EAAE,SAAS,CAAC,MAAM;IAC/B,aAAa,EAAE,SAAS,CAAC,MAAM;IAC/B,aAAa,EAAE,SAAS,CAAC,MAAM,EAAE,6CAA6C;IAC9E,WAAW,EAAE,SAAS,CAAC,MAAM;CAC9B,CAAC;AAEF,kDAAkD;AAClD,MAAM,UAAU,KAAK,CAAC,IAAU,EAAE,GAAe;IAC/C,OAAO,SAAS,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,GAAG,CAAC,CAAC;AAC9C,CAAC;AA4ED,4BAA4B;AAC5B,MAAM,UAAU,KAAK;IACnB,oEAAoE;IACpE,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAC/C,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,SAAiB,EAAE,GAAW;IACrE,OAAO;QACL,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE;QAC9B,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC;QACxE,UAAU,EAAE,EAAE;QACd,OAAO,EAAE,EAAE;QACX,KAAK,EAAE,EAAE;QACT,KAAK,EAAE,EAAE;KACV,CAAC;AACJ,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Import / export — get your secrets in and out.
3
+ *
4
+ * Import understands the common CSV shapes (generic, Bitwarden, Chrome/Firefox,
5
+ * KeePass) by sniffing the header row. Export writes a portable JSON or CSV —
6
+ * necessarily *plaintext*, so callers must warn loudly and clean up after.
7
+ *
8
+ * Part of RageVault.
9
+ * Crafted by SoyRage Agency — https://soyrage.es/
10
+ * MIT licensed (see LICENSE).
11
+ */
12
+ import { type Item, type VaultData } from "./model.js";
13
+ /** A minimal, correct CSV parser (RFC 4180-ish: quotes, escaped quotes, CRLF). */
14
+ export declare function parseCsv(text: string): string[][];
15
+ /** Import items from CSV text. Returns the parsed items + how columns mapped. */
16
+ export declare function importCsv(text: string): {
17
+ items: Item[];
18
+ mapped: Record<string, string>;
19
+ };
20
+ /** Import a Bitwarden JSON export (`{ items:[{ login:{...} }], folders:[...] }`). */
21
+ export declare function importBitwardenJson(j: Record<string, unknown>): Item[];
22
+ /** Import a Proton Pass JSON export (`{ vaults: { id: { items:[{ data:{...} }] } } }`). */
23
+ export declare function importProtonPassJson(j: Record<string, unknown>): Item[];
24
+ /**
25
+ * Auto-detect the export format and import: Bitwarden / Proton Pass JSON, a prior
26
+ * RageVault JSON, or CSV (1Password / Chrome / KeePass / generic).
27
+ */
28
+ export declare function importAny(content: string, _filename?: string): {
29
+ items: Item[];
30
+ source: string;
31
+ };
32
+ /** Export the vault to portable (PLAINTEXT) JSON. */
33
+ export declare function exportJson(data: VaultData): string;
34
+ /** Export items to (PLAINTEXT) CSV in the generic shape most tools import. */
35
+ export declare function exportCsv(data: VaultData): string;