@knpkv/herdr-fleet 0.3.0 → 0.5.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/README.md +17 -2
- package/dist/errors.d.ts +7 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +3 -0
- package/dist/errors.js.map +1 -1
- package/dist/hash.d.ts +131 -1
- package/dist/hash.d.ts.map +1 -1
- package/dist/hash.js +128 -4
- package/dist/hash.js.map +1 -1
- package/dist/model.d.ts +830 -12
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +145 -9
- package/dist/model.js.map +1 -1
- package/dist/response.d.ts +1 -1
- package/dist/response.d.ts.map +1 -1
- package/dist/service.d.ts +1795 -114
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +253 -43
- package/dist/service.js.map +1 -1
- package/dist/sqlite.d.ts +86 -0
- package/dist/sqlite.d.ts.map +1 -0
- package/dist/sqlite.js +148 -0
- package/dist/sqlite.js.map +1 -0
- package/dist/store.d.ts +1399 -13
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +24 -42
- package/dist/store.js.map +1 -1
- package/package.json +10 -6
package/dist/sqlite.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Private on-disk SQLite databases for Herdr stores.
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - **Refuse, never repair.** A state directory or database file that other users
|
|
7
|
+
* could write is refused, and left exactly as found. Repairing it would hide that
|
|
8
|
+
* someone could already have changed it.
|
|
9
|
+
* - **Owner-only once accepted.** The state directory ends `0700` and the database
|
|
10
|
+
* files `0600`; an existing directory is checked before it is restricted, so a
|
|
11
|
+
* writable one is refused rather than quietly fixed.
|
|
12
|
+
* - **Only the last path component is checked for a symlink.** Symlinked ancestors
|
|
13
|
+
* (macOS `/var`, a Nix-managed home) are normal and allowed; a database or state
|
|
14
|
+
* directory that is itself a link is refused.
|
|
15
|
+
* - **Two layers.** {@link preparePrivateDatabasePath} and
|
|
16
|
+
* {@link securePrivateDatabaseFiles} only touch the file system, so any SQLite
|
|
17
|
+
* client can use them; {@link openPrivateSqlite} adds the `node:sqlite` connection.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* const opened = yield* openPrivateSqlite(path, {
|
|
22
|
+
* initialize: (database) => database.exec("CREATE TABLE IF NOT EXISTS jobs (id TEXT PRIMARY KEY)")
|
|
23
|
+
* })
|
|
24
|
+
* // after each write, WAL may have created -wal/-shm:
|
|
25
|
+
* yield* opened.secureFiles
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @module
|
|
29
|
+
*/
|
|
30
|
+
import { Effect, FileSystem, Path, Schema } from "effect";
|
|
31
|
+
import { DatabaseSync } from "node:sqlite";
|
|
32
|
+
/** One step of opening or securing a private database failed; `operation` names the step. */
|
|
33
|
+
export class PrivateDatabaseError extends Schema.TaggedError()("PrivateDatabaseError", {
|
|
34
|
+
operation: Schema.String,
|
|
35
|
+
cause: Schema.Defect()
|
|
36
|
+
}) {
|
|
37
|
+
}
|
|
38
|
+
const failWith = (operation) => (cause) => new PrivateDatabaseError({ operation, cause });
|
|
39
|
+
const groupOrOtherWritable = (paths, mode) => paths.sep === "/" && (mode & 0o022) !== 0;
|
|
40
|
+
const databaseFiles = (path) => [path, `${path}-wal`, `${path}-shm`];
|
|
41
|
+
// Refuses `path` when it is itself a symlink — including a dangling one — or when
|
|
42
|
+
// its real path is not its real parent joined with its name. A component that does
|
|
43
|
+
// not exist yet is checked through its parent.
|
|
44
|
+
const verifyPathIdentity = (path, fileSystem, paths, operation) => fileSystem.readLink(path).pipe(Effect.matchEffect({
|
|
45
|
+
onFailure: (cause) => cause.reason._tag === "NotFound" || cause.reason._tag === "Unknown"
|
|
46
|
+
? Effect.void
|
|
47
|
+
: Effect.fail(new PrivateDatabaseError({ cause, operation: `${operation}.readlink` })),
|
|
48
|
+
onSuccess: (target) => Effect.fail(new PrivateDatabaseError({ cause: { path, target }, operation }))
|
|
49
|
+
}), Effect.andThen(fileSystem.exists(path).pipe(Effect.mapError(failWith(`${operation}.exists`)))), Effect.flatMap((exists) => {
|
|
50
|
+
const parent = paths.dirname(path);
|
|
51
|
+
if (!exists) {
|
|
52
|
+
return parent === path ? Effect.void : verifyPathIdentity(parent, fileSystem, paths, operation);
|
|
53
|
+
}
|
|
54
|
+
return Effect.all({
|
|
55
|
+
realPath: fileSystem.realPath(path).pipe(Effect.mapError(failWith(`${operation}.realpath`))),
|
|
56
|
+
realParentPath: fileSystem.realPath(parent).pipe(Effect.mapError(failWith(`${operation}.parent-realpath`)))
|
|
57
|
+
}).pipe(Effect.flatMap(({ realParentPath, realPath }) => {
|
|
58
|
+
const expectedPath = paths.join(realParentPath, paths.basename(path));
|
|
59
|
+
return realPath === expectedPath
|
|
60
|
+
? Effect.void
|
|
61
|
+
: Effect.fail(new PrivateDatabaseError({ cause: { expectedPath, path, realPath }, operation }));
|
|
62
|
+
}));
|
|
63
|
+
}));
|
|
64
|
+
/**
|
|
65
|
+
* Refuse `path` if it is itself a symlink or does not resolve to its real parent
|
|
66
|
+
* joined with its name; a missing component is checked through its parent.
|
|
67
|
+
* Failures carry `operation` (sub-steps append `.readlink`, `.exists`, …).
|
|
68
|
+
*/
|
|
69
|
+
export const verifyPrivatePathIdentity = Effect.fn("PrivateDatabase.verifyPathIdentity")(function* (path, operation) {
|
|
70
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
71
|
+
const paths = yield* Path.Path;
|
|
72
|
+
yield* verifyPathIdentity(path, fileSystem, paths, operation);
|
|
73
|
+
});
|
|
74
|
+
/**
|
|
75
|
+
* Make `path`'s directory and any existing database files safe to open, or refuse.
|
|
76
|
+
*
|
|
77
|
+
* Refuses a directory or file that is a symlink, the wrong type, or group/other
|
|
78
|
+
* writable, without changing it. Otherwise restricts the directory (created if
|
|
79
|
+
* missing) to `0700` and existing db/-wal/-shm files to `0600`.
|
|
80
|
+
*/
|
|
81
|
+
export const preparePrivateDatabasePath = Effect.fn("PrivateDatabase.prepare")(function* (path) {
|
|
82
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
83
|
+
const paths = yield* Path.Path;
|
|
84
|
+
const directory = paths.dirname(path);
|
|
85
|
+
yield* fileSystem.makeDirectory(directory, { recursive: true, mode: 0o700 }).pipe(Effect.mapError(failWith("open.directory")));
|
|
86
|
+
const directoryInfo = yield* fileSystem.stat(directory).pipe(Effect.mapError(failWith("open.directory.stat")));
|
|
87
|
+
if (directoryInfo.type !== "Directory" || groupOrOtherWritable(paths, directoryInfo.mode)) {
|
|
88
|
+
return yield* new PrivateDatabaseError({
|
|
89
|
+
cause: { directory, mode: directoryInfo.mode, type: directoryInfo.type },
|
|
90
|
+
operation: "open.directory.unsafe"
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
yield* verifyPathIdentity(directory, fileSystem, paths, "open.directory.path-identity");
|
|
94
|
+
yield* fileSystem.chmod(directory, 0o700).pipe(Effect.mapError(failWith("open.secureDirectory")));
|
|
95
|
+
yield* Effect.forEach(databaseFiles(path), (file) => verifyPathIdentity(file, fileSystem, paths, "open.path-identity").pipe(Effect.andThen(fileSystem.exists(file).pipe(Effect.mapError(failWith("open.file.exists")))), Effect.flatMap((exists) => {
|
|
96
|
+
if (!exists)
|
|
97
|
+
return Effect.void;
|
|
98
|
+
return fileSystem.stat(file).pipe(Effect.mapError(failWith("open.file.stat")), Effect.flatMap((info) => info.type !== "File" || groupOrOtherWritable(paths, info.mode)
|
|
99
|
+
? Effect.fail(new PrivateDatabaseError({
|
|
100
|
+
cause: { file, mode: info.mode, type: info.type },
|
|
101
|
+
operation: "open.file.unsafe"
|
|
102
|
+
}))
|
|
103
|
+
: fileSystem.chmod(file, 0o600).pipe(Effect.mapError(failWith("open.file.secure")))));
|
|
104
|
+
})), { discard: true });
|
|
105
|
+
});
|
|
106
|
+
/**
|
|
107
|
+
* Set the database file and its `-wal`/`-shm` siblings, whichever exist, to `0600`.
|
|
108
|
+
*
|
|
109
|
+
* SQLite creates the siblings lazily, so call this after writes as well as after
|
|
110
|
+
* opening. A sibling that has become a symlink is refused rather than followed.
|
|
111
|
+
*/
|
|
112
|
+
export const securePrivateDatabaseFiles = Effect.fn("PrivateDatabase.secureFiles")(function* (path) {
|
|
113
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
114
|
+
const paths = yield* Path.Path;
|
|
115
|
+
yield* Effect.forEach(databaseFiles(path), (file) => verifyPathIdentity(file, fileSystem, paths, "secure.path-identity").pipe(Effect.andThen(fileSystem.exists(file).pipe(Effect.mapError(failWith("secure.exists")))), Effect.flatMap((exists) => exists ? fileSystem.chmod(file, 0o600).pipe(Effect.mapError(failWith("secure.chmod"))) : Effect.void)), { discard: true });
|
|
116
|
+
});
|
|
117
|
+
/**
|
|
118
|
+
* Prepare `path` (see {@link preparePrivateDatabasePath}), open it in WAL mode,
|
|
119
|
+
* initialize the schema and secure the files. The caller owns the returned
|
|
120
|
+
* connection and must close it.
|
|
121
|
+
*/
|
|
122
|
+
export const openPrivateSqlite = Effect.fn("PrivateDatabase.openSqlite")(function* (path, options) {
|
|
123
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
124
|
+
const paths = yield* Path.Path;
|
|
125
|
+
yield* preparePrivateDatabasePath(path);
|
|
126
|
+
const database = yield* Effect.try({
|
|
127
|
+
try: () => {
|
|
128
|
+
const database = new DatabaseSync(path);
|
|
129
|
+
try {
|
|
130
|
+
if (options.busyTimeoutMillis !== undefined) {
|
|
131
|
+
database.exec(`PRAGMA busy_timeout = ${options.busyTimeoutMillis}`);
|
|
132
|
+
}
|
|
133
|
+
options.initialize(database);
|
|
134
|
+
database.exec("PRAGMA journal_mode = WAL");
|
|
135
|
+
return database;
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
database.close();
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
catch: failWith("open.database")
|
|
143
|
+
});
|
|
144
|
+
const secureFiles = securePrivateDatabaseFiles(path).pipe(Effect.provideService(FileSystem.FileSystem, fileSystem), Effect.provideService(Path.Path, paths));
|
|
145
|
+
yield* secureFiles.pipe(Effect.onError(() => Effect.sync(() => database.close())));
|
|
146
|
+
return { database, secureFiles };
|
|
147
|
+
});
|
|
148
|
+
//# sourceMappingURL=sqlite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../src/sqlite.ts"],"names":[],"mappings":"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"}
|