latticesql 2.2.4 → 3.0.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 +124 -146
- package/dist/cli.js +55503 -20083
- package/dist/index.cjs +47109 -8510
- package/dist/index.d.cts +595 -633
- package/dist/index.d.ts +595 -633
- package/dist/index.js +46867 -8304
- package/package.json +9 -9
package/dist/index.d.cts
CHANGED
|
@@ -766,6 +766,31 @@ interface ChangelogOptions {
|
|
|
766
766
|
/** Keep at most this many entries per row. */
|
|
767
767
|
maxEntriesPerRow?: number;
|
|
768
768
|
}
|
|
769
|
+
/**
|
|
770
|
+
* Provenance for a change — the per-viewer observation model's audit metadata.
|
|
771
|
+
* Every field is optional and additive: a plain edit carries none of it and
|
|
772
|
+
* behaves exactly as before. A *derived* value (e.g. an AI enrichment computed
|
|
773
|
+
* from source files) carries the `sourceRef` set that informed it, so the
|
|
774
|
+
* change-log records which authority produced the value rather than discarding
|
|
775
|
+
* it (the confused-deputy guard). Stage-0 persists this as audit metadata;
|
|
776
|
+
* later stages read it to fold per-viewer entities and cascade revocation.
|
|
777
|
+
*/
|
|
778
|
+
interface ChangeProvenance {
|
|
779
|
+
/** Source row/file id(s) that informed this value. Persisted as a JSON array
|
|
780
|
+
* in `__lattice_changelog.source_ref`. NOT a foreign key — purely an audit
|
|
781
|
+
* trail; a source may be deleted without violating anything. */
|
|
782
|
+
sourceRef?: string[] | string;
|
|
783
|
+
/** `ground_truth` — a direct edit; `derived` — computed from sources. */
|
|
784
|
+
changeKind?: 'ground_truth' | 'derived';
|
|
785
|
+
/** Reserved per-value audience. Omitted ⇒ the row's audience (no change). */
|
|
786
|
+
audience?: string;
|
|
787
|
+
/** Marks the source(s) sensitive (a future crypto-shred candidate). */
|
|
788
|
+
sourceSensitive?: boolean;
|
|
789
|
+
/** A prior changelog id this entry supersedes. */
|
|
790
|
+
supersededBy?: string;
|
|
791
|
+
/** Free-text reason (mirrors the legacy `reason` field). */
|
|
792
|
+
reason?: string;
|
|
793
|
+
}
|
|
769
794
|
/**
|
|
770
795
|
* A single entry in the change log returned by `history()` and
|
|
771
796
|
* `recentChanges()`.
|
|
@@ -789,6 +814,11 @@ interface ChangeEntry {
|
|
|
789
814
|
reason: string | null;
|
|
790
815
|
/** ISO timestamp of when the change was recorded. */
|
|
791
816
|
createdAt: string;
|
|
817
|
+
/** Source-set that informed a derived value (deserialized from `source_ref`).
|
|
818
|
+
* Null for plain edits + rows written before 3.0. */
|
|
819
|
+
sourceRef?: string[] | null;
|
|
820
|
+
/** `ground_truth` | `derived` provenance tag (null when unrecorded). */
|
|
821
|
+
changeKind?: 'ground_truth' | 'derived' | null;
|
|
792
822
|
}
|
|
793
823
|
interface SecurityOptions {
|
|
794
824
|
sanitize?: boolean;
|
|
@@ -932,6 +962,15 @@ interface TableDefinition {
|
|
|
932
962
|
* directly in code via `define()`.
|
|
933
963
|
*/
|
|
934
964
|
fieldTypes?: Record<string, string>;
|
|
965
|
+
/**
|
|
966
|
+
* Column name → audience identifier (Stage-0 per-viewer enrichment
|
|
967
|
+
* scaffolding). A column absent from this map has `row-audience` — visible to
|
|
968
|
+
* whoever can see the row, i.e. today's behavior. Populated from YAML field
|
|
969
|
+
* `audience:` specs (or a future code-level option). Recorded by the schema
|
|
970
|
+
* manager so a later stage can generate a per-column cell-masking view from
|
|
971
|
+
* it; unused in Stage-0, so it never changes behavior.
|
|
972
|
+
*/
|
|
973
|
+
columnAudience?: Record<string, string>;
|
|
935
974
|
/**
|
|
936
975
|
* Optional human description of what this entity represents. Surfaced in the
|
|
937
976
|
* GUI and given to the assistant's ingest classifier so it can decide which
|
|
@@ -1275,6 +1314,16 @@ interface CountOptions {
|
|
|
1275
1314
|
}
|
|
1276
1315
|
interface InitOptions {
|
|
1277
1316
|
migrations?: Migration[];
|
|
1317
|
+
/**
|
|
1318
|
+
* Open an already-provisioned database WITHOUT issuing any DDL — no
|
|
1319
|
+
* `CREATE TABLE`, no migrations, no FTS/changelog/embeddings setup. Used to
|
|
1320
|
+
* connect as a scoped, non-superuser cloud member: every table, migration,
|
|
1321
|
+
* and policy was installed by the cloud owner, and the member's role has no
|
|
1322
|
+
* CREATE/ALTER privilege, so applying the schema would fail. Declared tables
|
|
1323
|
+
* are introspected (best-effort) to populate the column cache; tables the
|
|
1324
|
+
* member can't see are skipped.
|
|
1325
|
+
*/
|
|
1326
|
+
introspectOnly?: boolean;
|
|
1278
1327
|
}
|
|
1279
1328
|
interface Migration {
|
|
1280
1329
|
version: number | string;
|
|
@@ -1624,6 +1673,64 @@ interface ReconcileResult extends RenderResult {
|
|
|
1624
1673
|
reverseSeedRequired: ReverseSeedDetection[];
|
|
1625
1674
|
}
|
|
1626
1675
|
|
|
1676
|
+
/**
|
|
1677
|
+
* Cryptographic erasure ("crypto-shred") for sources flagged sensitive.
|
|
1678
|
+
*
|
|
1679
|
+
* Un-sharing or deleting a source already removes its derived values from every
|
|
1680
|
+
* viewer's fold (the fold only includes observations whose sources are visible —
|
|
1681
|
+
* see ./fold.ts). But the bytes can linger in backups and WAL. For a legal /
|
|
1682
|
+
* GDPR "right to be forgotten", a source flagged `source_sensitive` gets a
|
|
1683
|
+
* stronger guarantee: every value derived from it is stored ENCRYPTED under a
|
|
1684
|
+
* key unique to that source. To forget the source you destroy its key — and the
|
|
1685
|
+
* derived values become unrecoverable everywhere the ciphertext exists, backups
|
|
1686
|
+
* included, because the key never lived in the row.
|
|
1687
|
+
*
|
|
1688
|
+
* The key store is pluggable so the keys can live somewhere shred-durable and
|
|
1689
|
+
* separate from the data (a KMS, a key table on a different retention policy).
|
|
1690
|
+
* The default in-memory store is for tests + single-process use.
|
|
1691
|
+
*/
|
|
1692
|
+
/** Holds the per-source AES keys. Destroying a key is the erasure operation. */
|
|
1693
|
+
interface SourceKeyStore {
|
|
1694
|
+
/** The key for a source, or undefined if it was never created / has been shredded. */
|
|
1695
|
+
get(sourceId: string): Buffer | undefined;
|
|
1696
|
+
/** The key for a source, creating a fresh random 256-bit key on first use. */
|
|
1697
|
+
getOrCreate(sourceId: string): Buffer;
|
|
1698
|
+
/** Destroy a source's key — irreversibly. Values sealed under it can no longer
|
|
1699
|
+
* be opened, anywhere. Idempotent. */
|
|
1700
|
+
destroy(sourceId: string): void;
|
|
1701
|
+
}
|
|
1702
|
+
/** In-memory {@link SourceKeyStore}. Keys vanish with the process — fine for
|
|
1703
|
+
* tests; production should persist keys in a shred-durable store. */
|
|
1704
|
+
declare class InMemorySourceKeyStore implements SourceKeyStore {
|
|
1705
|
+
private readonly keys;
|
|
1706
|
+
get(sourceId: string): Buffer | undefined;
|
|
1707
|
+
getOrCreate(sourceId: string): Buffer;
|
|
1708
|
+
destroy(sourceId: string): void;
|
|
1709
|
+
}
|
|
1710
|
+
/** Error thrown when opening a value whose source key has been shredded. */
|
|
1711
|
+
declare class SourceShreddedError extends Error {
|
|
1712
|
+
readonly sourceId: string;
|
|
1713
|
+
constructor(sourceId: string);
|
|
1714
|
+
}
|
|
1715
|
+
/**
|
|
1716
|
+
* Encrypt a derived value under its (sensitive) source's key, creating the key on
|
|
1717
|
+
* first use. The returned ciphertext is opaque without the source key.
|
|
1718
|
+
*/
|
|
1719
|
+
declare function sealUnderSource(plaintext: string, sourceId: string, store: SourceKeyStore): string;
|
|
1720
|
+
/**
|
|
1721
|
+
* Decrypt a value sealed under a source's key. Throws {@link SourceShreddedError}
|
|
1722
|
+
* if the source has been shredded (the key is gone) — the value is unrecoverable,
|
|
1723
|
+
* which is the intended "forgotten" state, not an error to paper over.
|
|
1724
|
+
*/
|
|
1725
|
+
declare function openUnderSource(ciphertext: string, sourceId: string, store: SourceKeyStore): string;
|
|
1726
|
+
/**
|
|
1727
|
+
* Cryptographically shred a source: destroy its key so every value sealed under
|
|
1728
|
+
* it is unrecoverable everywhere the ciphertext exists (live rows, backups, WAL).
|
|
1729
|
+
* Pair with the fold-level revocation (which removes the value from live views) —
|
|
1730
|
+
* this is the durable, backup-proof half for legally-sensitive sources.
|
|
1731
|
+
*/
|
|
1732
|
+
declare function shredSource(sourceId: string, store: SourceKeyStore): void;
|
|
1733
|
+
|
|
1627
1734
|
/**
|
|
1628
1735
|
* Initialise Lattice from a YAML config file instead of an explicit path.
|
|
1629
1736
|
*
|
|
@@ -1721,8 +1828,8 @@ declare class Lattice {
|
|
|
1721
1828
|
* Idempotent: a second call for an already-registered table is a no-op
|
|
1722
1829
|
* (the underlying CREATE TABLE IF NOT EXISTS is already idempotent at
|
|
1723
1830
|
* the DB level; this skip avoids the SchemaManager.define throw on
|
|
1724
|
-
* re-registration).
|
|
1725
|
-
*
|
|
1831
|
+
* re-registration). Useful for callers that may bootstrap their
|
|
1832
|
+
* internal tables on every session start.
|
|
1726
1833
|
*
|
|
1727
1834
|
* Throws if called before `init()` (use `define()` instead).
|
|
1728
1835
|
*/
|
|
@@ -1793,6 +1900,13 @@ declare class Lattice {
|
|
|
1793
1900
|
* SchemaManager default.
|
|
1794
1901
|
*/
|
|
1795
1902
|
getPrimaryKey(table: string): string[];
|
|
1903
|
+
/**
|
|
1904
|
+
* Per-column audience for a table (per-viewer enrichment) — column name →
|
|
1905
|
+
* audience identifier. A column absent from the map has `row-audience`
|
|
1906
|
+
* (visible to whoever can see the row). Empty until a column declares
|
|
1907
|
+
* `audience:`. Drives the generated cell-masking view.
|
|
1908
|
+
*/
|
|
1909
|
+
getColumnAudience(table: string): Record<string, string>;
|
|
1796
1910
|
/**
|
|
1797
1911
|
* Return the raw column declarations for a registered table, as
|
|
1798
1912
|
* passed to `define()` / `defineLate()`. Returns null for tables
|
|
@@ -1879,7 +1993,7 @@ declare class Lattice {
|
|
|
1879
1993
|
* names containing quotes, semicolons, whitespace, etc.
|
|
1880
1994
|
*/
|
|
1881
1995
|
private _assertIdent;
|
|
1882
|
-
insert(table: string, row: Row): Promise<string>;
|
|
1996
|
+
insert(table: string, row: Row, provenance?: ChangeProvenance): Promise<string>;
|
|
1883
1997
|
/**
|
|
1884
1998
|
* Insert a row and return the full inserted row (including auto-generated
|
|
1885
1999
|
* fields and defaults). Equivalent to `insert()` followed by `get()`.
|
|
@@ -1889,7 +2003,7 @@ declare class Lattice {
|
|
|
1889
2003
|
insertReturning(table: string, row: Row): Promise<Row>;
|
|
1890
2004
|
upsert(table: string, row: Row): Promise<string>;
|
|
1891
2005
|
upsertBy(table: string, col: string, val: unknown, row: Row): Promise<string>;
|
|
1892
|
-
update(table: string, id: PkLookup, row: Partial<Row
|
|
2006
|
+
update(table: string, id: PkLookup, row: Partial<Row>, provenance?: ChangeProvenance): Promise<void>;
|
|
1893
2007
|
/**
|
|
1894
2008
|
* Update a row and return the full updated row. Equivalent to `update()`
|
|
1895
2009
|
* followed by `get()`.
|
|
@@ -1897,7 +2011,43 @@ declare class Lattice {
|
|
|
1897
2011
|
* @since 0.17.0
|
|
1898
2012
|
*/
|
|
1899
2013
|
updateReturning(table: string, id: PkLookup, row: Partial<Row>): Promise<Row>;
|
|
1900
|
-
delete(table: string, id: PkLookup): Promise<void>;
|
|
2014
|
+
delete(table: string, id: PkLookup, provenance?: ChangeProvenance): Promise<void>;
|
|
2015
|
+
/**
|
|
2016
|
+
* Record a DERIVED observation about a row WITHOUT mutating the canonical row.
|
|
2017
|
+
* The canonical row stays broadly-visible ground truth; the observation carries
|
|
2018
|
+
* its provenance (the source-set it was derived from) and is folded into a
|
|
2019
|
+
* per-viewer entity at read time by {@link foldForViewer} — visible only to a
|
|
2020
|
+
* viewer who can reach every one of its sources. This is how an AI enrichment
|
|
2021
|
+
* lands a per-viewer value without leaking it into the shared row, and without
|
|
2022
|
+
* moving the row's `updated_at` (so a viewer who can't see the source can't even
|
|
2023
|
+
* detect that the enrichment exists). `changes` maps column → derived value.
|
|
2024
|
+
*/
|
|
2025
|
+
/** Ensure the observation substrate (`__lattice_changelog`) exists. Cloud setup
|
|
2026
|
+
* calls this before `enableChangelogRls` so the table is present to secure
|
|
2027
|
+
* even if nothing has written an observation yet. Idempotent. */
|
|
2028
|
+
ensureObservationSubstrate(): Promise<void>;
|
|
2029
|
+
observe(table: string, id: PkLookup, changes: Record<string, unknown>, provenance?: ChangeProvenance, opts?: {
|
|
2030
|
+
keyStore?: SourceKeyStore;
|
|
2031
|
+
}): Promise<void>;
|
|
2032
|
+
/**
|
|
2033
|
+
* Compile the per-viewer view of a row: the ground-truth canonical row with the
|
|
2034
|
+
* DERIVED observations the viewer is allowed to see folded on top (latest
|
|
2035
|
+
* audience-visible observation per attribute wins). A derived value is visible
|
|
2036
|
+
* only when the viewer can reach every source it came from, so un-sharing a
|
|
2037
|
+
* source reverts the value with no residue. `visibleSources` is the set of
|
|
2038
|
+
* source ids the viewer can see; omit it (or pass `'all'`) for the local
|
|
2039
|
+
* single-user case where you see everything. Returns null if the row is absent.
|
|
2040
|
+
*/
|
|
2041
|
+
foldForViewer(table: string, id: PkLookup, opts?: {
|
|
2042
|
+
visibleSources?: Iterable<string> | 'all';
|
|
2043
|
+
keyStore?: SourceKeyStore;
|
|
2044
|
+
}): Promise<Row | null>;
|
|
2045
|
+
/** Open any crypto-sealed values in a derived observation's `changes`. Returns
|
|
2046
|
+
* the plaintext changes, or `null` if a sealed value can't be opened because
|
|
2047
|
+
* its source's key was shredded (the value is gone for good). Values that
|
|
2048
|
+
* aren't sealed pass through; with no key store, sealed values can't be read,
|
|
2049
|
+
* so the observation is dropped (returns null). */
|
|
2050
|
+
private _openSealedObservation;
|
|
1901
2051
|
get(table: string, id: PkLookup): Promise<Row | null>;
|
|
1902
2052
|
/**
|
|
1903
2053
|
* Upsert a record by natural key. If a non-deleted record with the given
|
|
@@ -1969,76 +2119,6 @@ declare class Lattice {
|
|
|
1969
2119
|
*/
|
|
1970
2120
|
search(table: string, query: string, opts?: SearchOptions): Promise<SearchResult[]>;
|
|
1971
2121
|
query(table: string, opts?: QueryOptions): Promise<Row[]>;
|
|
1972
|
-
/**
|
|
1973
|
-
* Row-level-security list read for Lattice Teams (2.2). Returns only the
|
|
1974
|
-
* rows of `table` that `userId` may see in team `teamId`, evaluated
|
|
1975
|
-
* entirely in SQL (indexed, bounded — never "load every row then filter
|
|
1976
|
-
* in JS"). A row is visible iff it has a `__lattice_row_acl` entry owned by
|
|
1977
|
-
* the user or marked 'everyone', or a 'custom' entry with a matching
|
|
1978
|
-
* `__lattice_row_grants` row, OR it has no ACL entry at all and the caller
|
|
1979
|
-
* passes `noAclVisible` (the table default is 'everyone', or the user owns
|
|
1980
|
-
* the table — the pre-2.2 / never-narrowed case). Soft-deleted rows are
|
|
1981
|
-
* excluded by default; results reuse the same decrypt path as `query()`.
|
|
1982
|
-
*
|
|
1983
|
-
* The ACL predicate joins on the table's primary-key column cast to TEXT
|
|
1984
|
-
* (ACL pks are stored as TEXT), so it is correct regardless of the user
|
|
1985
|
-
* table's pk type and works on both SQLite and Postgres. The teams layer's
|
|
1986
|
-
* `listVisibleRows` (src/teams/row-access.ts) is the intended caller.
|
|
1987
|
-
*/
|
|
1988
|
-
queryVisible(table: string, opts: {
|
|
1989
|
-
teamId: string;
|
|
1990
|
-
userId: string;
|
|
1991
|
-
/**
|
|
1992
|
-
* Whether rows with NO `__lattice_row_acl` entry are visible to this
|
|
1993
|
-
* user — true when the table default is 'everyone' OR the user owns the
|
|
1994
|
-
* table (the pre-2.2 / never-narrowed case). Resolved by the teams layer
|
|
1995
|
-
* (`listVisibleRows`); defaults to false, i.e. only rows with an explicit
|
|
1996
|
-
* ACL entry granting access are returned.
|
|
1997
|
-
*/
|
|
1998
|
-
noAclVisible?: boolean;
|
|
1999
|
-
/** Soft-delete handling: 'exclude' (default), 'only' (trash), 'any'. */
|
|
2000
|
-
deleted?: 'exclude' | 'only' | 'any';
|
|
2001
|
-
limit?: number;
|
|
2002
|
-
offset?: number;
|
|
2003
|
-
orderBy?: string;
|
|
2004
|
-
orderDir?: 'asc' | 'desc';
|
|
2005
|
-
}): Promise<Row[]>;
|
|
2006
|
-
/**
|
|
2007
|
-
* Visible-row counts for MANY tables in a single round-trip, using the same
|
|
2008
|
-
* ACL predicate as {@link queryVisible} — so dashboard tiles agree with what
|
|
2009
|
-
* the rows view lists and a physical count never reveals the existence or
|
|
2010
|
-
* volume of rows the user can't see. One aggregated
|
|
2011
|
-
* `SELECT (SELECT COUNT(*) …) AS c0, …` statement (no per-table fan-out, so
|
|
2012
|
-
* a session pooler with few slots survives concurrent refreshes), capped at
|
|
2013
|
-
* 50 tables per pass; overflow is logged and skipped (no silent truncation)
|
|
2014
|
-
* and those tables count as absent — the caller renders "—". Soft-deleted
|
|
2015
|
-
* rows are excluded wherever the table carries `deleted_at`, matching the
|
|
2016
|
-
* default rows view.
|
|
2017
|
-
*/
|
|
2018
|
-
countVisibleMany(specs: {
|
|
2019
|
-
table: string;
|
|
2020
|
-
noAclVisible: boolean;
|
|
2021
|
-
}[], opts: {
|
|
2022
|
-
teamId: string;
|
|
2023
|
-
userId: string;
|
|
2024
|
-
}): Promise<Map<string, number>>;
|
|
2025
|
-
/**
|
|
2026
|
-
* Hosted-sync change-log pull, filtered per recipient for 2.2 row-level
|
|
2027
|
-
* security (the hosted server's sole enforcement mechanism). Returns
|
|
2028
|
-
* `__lattice_change_log` rows with seq > `since` for team `teamId` that
|
|
2029
|
-
* `userId` is permitted to receive:
|
|
2030
|
-
* - targeted envelopes (`recipient_user_id = userId`), plus
|
|
2031
|
-
* - broadcast envelopes (`recipient_user_id IS NULL`) that are either
|
|
2032
|
-
* table-level (`pk IS NULL` — schema / unshare, delivered to all) or
|
|
2033
|
-
* whose row is currently visible to the user via `__lattice_row_acl` /
|
|
2034
|
-
* `__lattice_row_grants` (or has no ACL entry and the table defaults to
|
|
2035
|
-
* 'everyone').
|
|
2036
|
-
* Ordered by seq, capped at `limit`. Raw SQL because the predicate needs
|
|
2037
|
-
* OR / EXISTS that the `query()` API can't express; bounded by the seq
|
|
2038
|
-
* window and indexed ACL point-lookups. Mirrors {@link queryVisible}'s
|
|
2039
|
-
* visibility logic so a member never pulls the bytes of a row they can't see.
|
|
2040
|
-
*/
|
|
2041
|
-
listChangesForRecipient(teamId: string, since: number, userId: string, limit: number): Promise<Row[]>;
|
|
2042
2122
|
count(table: string, opts?: CountOptions): Promise<number>;
|
|
2043
2123
|
render(outputDir: string): Promise<RenderResult>;
|
|
2044
2124
|
sync(outputDir: string): Promise<SyncResult>;
|
|
@@ -2167,10 +2247,31 @@ declare class Lattice {
|
|
|
2167
2247
|
* shouldn't block the write completion.
|
|
2168
2248
|
*/
|
|
2169
2249
|
private _syncEmbedding;
|
|
2170
|
-
/**
|
|
2250
|
+
/**
|
|
2251
|
+
* Create the __lattice_changelog table and index. This is the single,
|
|
2252
|
+
* canonical change-log substrate (the dead `__lattice_change_log` team-sync
|
|
2253
|
+
* envelope was removed in 3.0). Beyond the field-level delta columns it
|
|
2254
|
+
* carries provenance columns for the per-viewer observation model:
|
|
2255
|
+
* `source_ref` (the source-set that informed a derived value),
|
|
2256
|
+
* `change_kind` (`ground_truth` | `derived`), `superseded_by`, `audience`
|
|
2257
|
+
* (defaults to row audience), and `source_sensitive` (crypto-shred flag).
|
|
2258
|
+
* All are additive + nullable (or defaulted) — Stage-0 metadata, no behavior
|
|
2259
|
+
* change until later stages read them.
|
|
2260
|
+
*/
|
|
2261
|
+
/** Whether `__lattice_changelog` physically exists (read-only; no DDL), so a
|
|
2262
|
+
* scoped member can decide there are no observations without trying to create
|
|
2263
|
+
* the table. */
|
|
2264
|
+
private _changelogTableExists;
|
|
2171
2265
|
private _ensureChangelogTable;
|
|
2172
|
-
/** Append a changelog entry if the table has changelog enabled.
|
|
2266
|
+
/** Append a changelog entry if the table has changelog enabled. The optional
|
|
2267
|
+
* `prov` carries the per-viewer observation provenance (source-set, kind,
|
|
2268
|
+
* audience, …); when omitted the entry behaves exactly as a pre-3.0 entry. */
|
|
2173
2269
|
private _appendChangelog;
|
|
2270
|
+
/** The ungated change-log INSERT. `_appendChangelog` wraps it with the
|
|
2271
|
+
* changelog-enabled gate; `observe()` calls it directly (an observation is an
|
|
2272
|
+
* explicit, always-recorded write to the substrate). The change-log table must
|
|
2273
|
+
* exist already. */
|
|
2274
|
+
private _writeChangelogRow;
|
|
2174
2275
|
/** Prune changelog entries based on retention policy. */
|
|
2175
2276
|
private _pruneChangelog;
|
|
2176
2277
|
/** Parse a raw changelog DB row into a ChangeEntry. */
|
|
@@ -2286,6 +2387,15 @@ interface LatticeFieldDef {
|
|
|
2286
2387
|
* (e.g. `assignee_id: { ref: user }` → relation name `assignee`).
|
|
2287
2388
|
*/
|
|
2288
2389
|
ref?: string;
|
|
2390
|
+
/**
|
|
2391
|
+
* Per-column audience (Stage-0 scaffolding for the per-viewer enrichment
|
|
2392
|
+
* model). Names who may see this column's value in a cloud. Omitted ⇒
|
|
2393
|
+
* `row-audience` — the value is visible to exactly whoever can see the row,
|
|
2394
|
+
* which is today's behavior, so leaving it unset changes nothing. Later
|
|
2395
|
+
* stages parse a richer grammar (e.g. `subject+role:hr`) and generate a
|
|
2396
|
+
* cell-masking view from it; Stage-0 only records the metadata.
|
|
2397
|
+
*/
|
|
2398
|
+
audience?: string;
|
|
2289
2399
|
}
|
|
2290
2400
|
/**
|
|
2291
2401
|
* Inline render spec inside YAML — a flat object alternative to `TemplateRenderSpec`.
|
|
@@ -3062,6 +3172,80 @@ declare function hashFile(srcPath: string): Promise<string>;
|
|
|
3062
3172
|
*/
|
|
3063
3173
|
declare function attachBlob(srcPath: string, latticeRoot: string): Promise<BlobMetadata>;
|
|
3064
3174
|
|
|
3175
|
+
/**
|
|
3176
|
+
* S3 object storage for file blobs (cloud workspaces). Bytes uploaded to a cloud
|
|
3177
|
+
* are written to S3 under a content-addressed key so every member who can see the
|
|
3178
|
+
* `files` row can pull them down — see `docs/cloud.md`. `@aws-sdk/client-s3` is an
|
|
3179
|
+
* OPTIONAL dependency, lazy-imported the same way `sharp` is, so a build without it
|
|
3180
|
+
* still loads: callers catch {@link S3UnavailableError} and fall back to local-only
|
|
3181
|
+
* blobs rather than failing.
|
|
3182
|
+
*
|
|
3183
|
+
* Access control is NOT enforced here — it rides entirely on the `files`-row
|
|
3184
|
+
* Postgres RLS at the serve route (`files-routes.ts`): a member only ever learns a
|
|
3185
|
+
* key for a row they can SELECT, keys are unguessable (sha256), and the bucket
|
|
3186
|
+
* credential is least-privilege (GetObject/PutObject only, no ListBucket). See the
|
|
3187
|
+
* security notes in `docs/cloud.md`.
|
|
3188
|
+
*/
|
|
3189
|
+
/** A minimal remote blob store — the only surface the upload + serve paths use. */
|
|
3190
|
+
interface RemoteBlobStore {
|
|
3191
|
+
/** Idempotent upload under `key` (same content ⇒ same key ⇒ same object). */
|
|
3192
|
+
put(key: string, body: Buffer, opts?: {
|
|
3193
|
+
contentType?: string;
|
|
3194
|
+
}): Promise<void>;
|
|
3195
|
+
/** Stream the object's bytes back (to pipe into an HTTP response). */
|
|
3196
|
+
get(key: string): Promise<NodeJS.ReadableStream>;
|
|
3197
|
+
/** Whether an object exists under `key`. */
|
|
3198
|
+
exists(key: string): Promise<boolean>;
|
|
3199
|
+
}
|
|
3200
|
+
/** Connection config for an S3 (or S3-compatible) bucket. Credentials are optional
|
|
3201
|
+
* — when omitted, the AWS default credential chain (env / shared config / IAM
|
|
3202
|
+
* role) is used. `endpoint` (+ path-style) targets R2 / MinIO / LocalStack. */
|
|
3203
|
+
interface S3StoreConfig {
|
|
3204
|
+
bucket: string;
|
|
3205
|
+
region: string;
|
|
3206
|
+
prefix: string;
|
|
3207
|
+
endpoint?: string;
|
|
3208
|
+
credentials?: {
|
|
3209
|
+
accessKeyId: string;
|
|
3210
|
+
secretAccessKey: string;
|
|
3211
|
+
};
|
|
3212
|
+
}
|
|
3213
|
+
/** Thrown when `@aws-sdk/client-s3` is not installed. Callers degrade to
|
|
3214
|
+
* local-only behavior; they do not 500. */
|
|
3215
|
+
declare class S3UnavailableError extends Error {
|
|
3216
|
+
constructor(message: string);
|
|
3217
|
+
}
|
|
3218
|
+
/** Content-addressed object key: `<prefix>/<sha256>` (POSIX separators, stable
|
|
3219
|
+
* across machines + idempotent). A leading/trailing slash on the prefix is
|
|
3220
|
+
* normalized away. */
|
|
3221
|
+
declare function s3Key(prefix: string, sha256: string): string;
|
|
3222
|
+
/**
|
|
3223
|
+
* Build a {@link RemoteBlobStore} backed by S3. Throws {@link S3UnavailableError}
|
|
3224
|
+
* if the optional `@aws-sdk/client-s3` dependency is absent — the lazy import
|
|
3225
|
+
* mirrors the `sharp` pattern so the module loads without it.
|
|
3226
|
+
*/
|
|
3227
|
+
declare function createS3Store(cfg: S3StoreConfig): Promise<RemoteBlobStore>;
|
|
3228
|
+
|
|
3229
|
+
/**
|
|
3230
|
+
* The S3 config for a cloud workspace, resolved per-member. `enabled` gates the
|
|
3231
|
+
* whole feature; the rest is the {@link S3StoreConfig} the store consumes. Stored
|
|
3232
|
+
* machine-local + encrypted (see user-config `s3-config.enc`), or supplied via env
|
|
3233
|
+
* for headless / CI.
|
|
3234
|
+
*/
|
|
3235
|
+
interface S3Config extends S3StoreConfig {
|
|
3236
|
+
enabled: boolean;
|
|
3237
|
+
}
|
|
3238
|
+
/** Extract the cloud workspace LABEL from a config's `db:` line
|
|
3239
|
+
* (`db: ${LATTICE_DB:label}`), so the S3 config can be keyed by it. Null when the
|
|
3240
|
+
* config isn't a labelled cloud connection. */
|
|
3241
|
+
declare function activeWorkspaceLabel(configPath: string): string | null;
|
|
3242
|
+
/**
|
|
3243
|
+
* Resolve the active workspace's S3 config: the per-member machine-local config
|
|
3244
|
+
* keyed by the workspace label, else the environment. Returns null (S3 off) when
|
|
3245
|
+
* neither is configured — so a non-cloud / S3-disabled workspace is untouched.
|
|
3246
|
+
*/
|
|
3247
|
+
declare function resolveActiveS3Config(configPath: string | undefined): S3Config | null;
|
|
3248
|
+
|
|
3065
3249
|
/**
|
|
3066
3250
|
* Machine-local lattice user config — small files that live outside any
|
|
3067
3251
|
* single Lattice DB so a user's identity, encryption master key, saved
|
|
@@ -3416,7 +3600,7 @@ declare function importLegacyUserConfig(root: string): MigrateResult;
|
|
|
3416
3600
|
* local and cloud sources share one set of utilities.
|
|
3417
3601
|
*/
|
|
3418
3602
|
type RefKind = 'blob' | 'local_ref' | 'cloud_ref';
|
|
3419
|
-
type RefProvider = 'fs' | 'web' | 'gdrive';
|
|
3603
|
+
type RefProvider = 'fs' | 'web' | 'gdrive' | 's3';
|
|
3420
3604
|
/** The subset of a `files` row the resolver reads. */
|
|
3421
3605
|
interface FilesRow {
|
|
3422
3606
|
id?: string;
|
|
@@ -3578,520 +3762,6 @@ declare function referenceUrl(url: string, opts?: {
|
|
|
3578
3762
|
allowPrivate?: boolean;
|
|
3579
3763
|
}): Promise<ReferenceMetadata>;
|
|
3580
3764
|
|
|
3581
|
-
type ColumnType = 'TEXT' | 'INTEGER' | 'REAL' | 'BLOB' | 'JSONB';
|
|
3582
|
-
interface ColumnSpec {
|
|
3583
|
-
type: ColumnType;
|
|
3584
|
-
notNull?: boolean;
|
|
3585
|
-
pk?: true;
|
|
3586
|
-
default?: string;
|
|
3587
|
-
}
|
|
3588
|
-
interface SchemaSpec {
|
|
3589
|
-
columns: Record<string, ColumnSpec>;
|
|
3590
|
-
primaryKey: string | string[];
|
|
3591
|
-
tableConstraints?: string[];
|
|
3592
|
-
relations?: Record<string, {
|
|
3593
|
-
kind: 'belongsTo';
|
|
3594
|
-
table: string;
|
|
3595
|
-
foreignKey: string;
|
|
3596
|
-
}>;
|
|
3597
|
-
schemaVersion: number;
|
|
3598
|
-
}
|
|
3599
|
-
|
|
3600
|
-
/**
|
|
3601
|
-
* Cloud-connect probe — non-destructive inspection of a candidate
|
|
3602
|
-
* target Lattice (typically a BYO Postgres URL the user wants to
|
|
3603
|
-
* connect to). Used by the GUI's "Migrate to cloud" and "Connect to
|
|
3604
|
-
* existing cloud" wizards to surface team-membership requirements
|
|
3605
|
-
* before the user commits, but exported as a public API so library
|
|
3606
|
-
* consumers can pre-flight the same check.
|
|
3607
|
-
*
|
|
3608
|
-
* The function opens a short-lived Lattice against the URL, reads
|
|
3609
|
-
* the singleton `__lattice_team_identity` row (if any), then closes.
|
|
3610
|
-
* It does NOT mutate the target — `init()` does run, which applies
|
|
3611
|
-
* the base schema, but no rows are inserted and no GUI/native
|
|
3612
|
-
* registration happens here.
|
|
3613
|
-
*/
|
|
3614
|
-
interface CloudProbeResult {
|
|
3615
|
-
/** True iff a Lattice could be opened + init()'d successfully against the URL. */
|
|
3616
|
-
reachable: boolean;
|
|
3617
|
-
/** Adapter dialect of the probed URL. Useful for the GUI to label cards. */
|
|
3618
|
-
dialect: 'sqlite' | 'postgres';
|
|
3619
|
-
/** True iff the probed Lattice has a populated `__lattice_team_identity` row. */
|
|
3620
|
-
teamEnabled: boolean;
|
|
3621
|
-
/** Team name from `__lattice_team_identity.team_name`, if `teamEnabled`. */
|
|
3622
|
-
teamName?: string;
|
|
3623
|
-
/** Underlying error message when `reachable: false`. */
|
|
3624
|
-
error?: string;
|
|
3625
|
-
}
|
|
3626
|
-
/**
|
|
3627
|
-
* Probe a candidate Lattice URL for reachability + team status.
|
|
3628
|
-
*
|
|
3629
|
-
* Implementation note: this opens a real Lattice with no schema
|
|
3630
|
-
* registered beyond what `init()` applies internally, then queries
|
|
3631
|
-
* a single row. The `__lattice_team_identity` table doesn't exist on
|
|
3632
|
-
* untouched DBs — the query falls through to a "table not found"
|
|
3633
|
-
* which we treat as `teamEnabled: false`. On a Lattice that's been
|
|
3634
|
-
* through `lattice gui` (which registers the team-identity table
|
|
3635
|
-
* during openConfig), the table exists but may be empty, also
|
|
3636
|
-
* `teamEnabled: false`.
|
|
3637
|
-
*
|
|
3638
|
-
* Never throws. Errors are returned in the result's `error` field
|
|
3639
|
-
* with `reachable: false`.
|
|
3640
|
-
*/
|
|
3641
|
-
declare function probeCloud(targetUrl: string): Promise<CloudProbeResult>;
|
|
3642
|
-
|
|
3643
|
-
/**
|
|
3644
|
-
* Local-side client for a Lattice Teams cloud. Wraps the cloud HTTP API
|
|
3645
|
-
* and persists per-team connection metadata into the local lattice's
|
|
3646
|
-
* `__lattice_team_connections` table.
|
|
3647
|
-
*
|
|
3648
|
-
* Phase 2 covers identity + team management: register, redeem-invite,
|
|
3649
|
-
* list-teams, create-team, delete-team, list-members, invite, kick.
|
|
3650
|
-
* Phases 3 and 4 add object sharing, row link/unlink, and the polling
|
|
3651
|
-
* sync loop.
|
|
3652
|
-
*/
|
|
3653
|
-
interface TeamSummary {
|
|
3654
|
-
id: string;
|
|
3655
|
-
name: string;
|
|
3656
|
-
role: string;
|
|
3657
|
-
}
|
|
3658
|
-
interface RegisterResponse {
|
|
3659
|
-
user: {
|
|
3660
|
-
id: string;
|
|
3661
|
-
email: string;
|
|
3662
|
-
name: string;
|
|
3663
|
-
};
|
|
3664
|
-
raw_token: string;
|
|
3665
|
-
team: TeamSummary;
|
|
3666
|
-
}
|
|
3667
|
-
interface RedeemResponse {
|
|
3668
|
-
user: {
|
|
3669
|
-
id: string;
|
|
3670
|
-
email: string;
|
|
3671
|
-
name: string;
|
|
3672
|
-
};
|
|
3673
|
-
raw_token: string;
|
|
3674
|
-
team: {
|
|
3675
|
-
id: string;
|
|
3676
|
-
name: string;
|
|
3677
|
-
};
|
|
3678
|
-
}
|
|
3679
|
-
interface MemberSummary {
|
|
3680
|
-
user_id: string;
|
|
3681
|
-
email: string | null;
|
|
3682
|
-
name: string | null;
|
|
3683
|
-
role: string;
|
|
3684
|
-
joined_at: string;
|
|
3685
|
-
}
|
|
3686
|
-
/**
|
|
3687
|
-
* An invitation that has been issued but not yet redeemed — surfaced in the
|
|
3688
|
-
* member list so the owner can see who's been invited but hasn't joined.
|
|
3689
|
-
*/
|
|
3690
|
-
interface PendingInvitationSummary {
|
|
3691
|
-
id: string;
|
|
3692
|
-
invitee_email: string;
|
|
3693
|
-
invited_at: string;
|
|
3694
|
-
expires_at: string | null;
|
|
3695
|
-
expired: boolean;
|
|
3696
|
-
}
|
|
3697
|
-
interface InviteResponse {
|
|
3698
|
-
id: string;
|
|
3699
|
-
raw_token: string;
|
|
3700
|
-
expires_at: string;
|
|
3701
|
-
team_name: string;
|
|
3702
|
-
invitee_email: string;
|
|
3703
|
-
}
|
|
3704
|
-
interface TeamConnection {
|
|
3705
|
-
team_id: string;
|
|
3706
|
-
team_name: string;
|
|
3707
|
-
cloud_url: string;
|
|
3708
|
-
my_user_id: string;
|
|
3709
|
-
api_token: string;
|
|
3710
|
-
joined_at: string;
|
|
3711
|
-
}
|
|
3712
|
-
interface SharedObjectSummary {
|
|
3713
|
-
table: string;
|
|
3714
|
-
schema_version: number;
|
|
3715
|
-
schema_spec: SchemaSpec;
|
|
3716
|
-
created_by_user_id: string;
|
|
3717
|
-
created_at: string;
|
|
3718
|
-
updated_at: string;
|
|
3719
|
-
}
|
|
3720
|
-
interface ShareObjectResponse {
|
|
3721
|
-
table: string;
|
|
3722
|
-
schema_version: number;
|
|
3723
|
-
seq: number;
|
|
3724
|
-
schema_spec: SchemaSpec;
|
|
3725
|
-
}
|
|
3726
|
-
interface ChangeEnvelope {
|
|
3727
|
-
seq: number;
|
|
3728
|
-
table_name: string | null;
|
|
3729
|
-
pk: string | null;
|
|
3730
|
-
/**
|
|
3731
|
-
* Cloud-emitted ops are `schema`/`unshare`/`link`/`unlink`/`upsert`/`delete`.
|
|
3732
|
-
* `divergence` is a client-side-only marker the puller writes into the DLQ
|
|
3733
|
-
* when a non-owner local edit was overwritten by an owner update — it is
|
|
3734
|
-
* never emitted by the cloud, and applying it is a no-op (the LWW overwrite
|
|
3735
|
-
* already happened; the entry exists so the lost local content is visible).
|
|
3736
|
-
*/
|
|
3737
|
-
op: 'schema' | 'unshare' | 'link' | 'unlink' | 'upsert' | 'delete' | 'divergence';
|
|
3738
|
-
payload: unknown;
|
|
3739
|
-
owner_user_id: string | null;
|
|
3740
|
-
created_at: string;
|
|
3741
|
-
}
|
|
3742
|
-
/**
|
|
3743
|
-
* A parsed dead-letter-queue entry — a change envelope that failed to apply
|
|
3744
|
-
* during a pull (or a non-owner-overwrite divergence notice), surfaced so an
|
|
3745
|
-
* operator can inspect and retry instead of it sitting invisible behind a
|
|
3746
|
-
* count. See {@link TeamsClient.listDlq}.
|
|
3747
|
-
*/
|
|
3748
|
-
interface DlqEntry {
|
|
3749
|
-
id: string;
|
|
3750
|
-
team_id: string;
|
|
3751
|
-
/** Table the failed envelope targeted (null for schema-level ops). */
|
|
3752
|
-
table_name: string | null;
|
|
3753
|
-
/** Primary key the failed envelope targeted (null for schema-level ops). */
|
|
3754
|
-
pk: string | null;
|
|
3755
|
-
/** The envelope op, e.g. `upsert` / `link` / `divergence`. */
|
|
3756
|
-
op: string;
|
|
3757
|
-
/** The error that caused the envelope to land in the DLQ. */
|
|
3758
|
-
error: string;
|
|
3759
|
-
created_at: string;
|
|
3760
|
-
/** The full envelope as stored, for retry / inspection. */
|
|
3761
|
-
envelope: ChangeEnvelope;
|
|
3762
|
-
}
|
|
3763
|
-
interface DlqRetryResult {
|
|
3764
|
-
retried: number;
|
|
3765
|
-
succeeded: number;
|
|
3766
|
-
failed: number;
|
|
3767
|
-
}
|
|
3768
|
-
interface SyncStatus {
|
|
3769
|
-
team_id: string;
|
|
3770
|
-
team_name: string;
|
|
3771
|
-
last_change_seq: number | null;
|
|
3772
|
-
outbox_depth: number;
|
|
3773
|
-
outbox_failing: number;
|
|
3774
|
-
dlq_depth: number;
|
|
3775
|
-
local_links: number;
|
|
3776
|
-
}
|
|
3777
|
-
interface PullResult {
|
|
3778
|
-
applied: number;
|
|
3779
|
-
last_seq: number;
|
|
3780
|
-
dlq_count: number;
|
|
3781
|
-
}
|
|
3782
|
-
interface PushResult {
|
|
3783
|
-
pushed: number;
|
|
3784
|
-
failed: number;
|
|
3785
|
-
}
|
|
3786
|
-
interface SyncSharedSchemasResult {
|
|
3787
|
-
applied: {
|
|
3788
|
-
table: string;
|
|
3789
|
-
schema_version: number;
|
|
3790
|
-
}[];
|
|
3791
|
-
conflicts: {
|
|
3792
|
-
table: string;
|
|
3793
|
-
reason: string;
|
|
3794
|
-
}[];
|
|
3795
|
-
}
|
|
3796
|
-
declare class TeamsClient {
|
|
3797
|
-
private readonly local;
|
|
3798
|
-
private _tablesReady;
|
|
3799
|
-
/**
|
|
3800
|
-
* Set during a pull's envelope application so the local write-hook
|
|
3801
|
-
* skips outbox insertion — otherwise B's pull of A's change would
|
|
3802
|
-
* push it right back to the cloud.
|
|
3803
|
-
*/
|
|
3804
|
-
private _isReplaying;
|
|
3805
|
-
/** Tables for which a sync write-hook has already been registered. */
|
|
3806
|
-
private readonly _hookedTables;
|
|
3807
|
-
constructor(local: Lattice);
|
|
3808
|
-
/**
|
|
3809
|
-
* Lazy-register the local `__lattice_team_connections` table on first
|
|
3810
|
-
* use. `defineLate` is idempotent, so calling this on every session
|
|
3811
|
-
* start is safe.
|
|
3812
|
-
*/
|
|
3813
|
-
ensureLocalTables(): Promise<void>;
|
|
3814
|
-
/**
|
|
3815
|
-
* Bootstrap-register on a fresh cloud and create the team in one
|
|
3816
|
-
* atomic call. The cloud rejects this once any user exists (subsequent
|
|
3817
|
-
* members join via `redeemInvite`). Returns the new user + bearer
|
|
3818
|
-
* token + team summary so the caller can immediately save a
|
|
3819
|
-
* connection.
|
|
3820
|
-
*
|
|
3821
|
-
* @param teamName The workspace display name (stored as `team_name` for
|
|
3822
|
-
* backward compatibility — a cloud IS a workspace with members).
|
|
3823
|
-
*/
|
|
3824
|
-
register(cloudUrl: string, email: string, name: string, teamName: string): Promise<RegisterResponse>;
|
|
3825
|
-
redeemInvite(cloudUrl: string, inviteToken: string, email: string, name: string): Promise<RedeemResponse>;
|
|
3826
|
-
/**
|
|
3827
|
-
* Connect a local project to an existing cloud DB by URL. Probes
|
|
3828
|
-
* the target for team status first; if it's a teams DB, the caller
|
|
3829
|
-
* must pass `invite_token` + identity (email/name) and the method
|
|
3830
|
-
* will redeem the invite and save the resulting bearer to
|
|
3831
|
-
* `~/.lattice/keys/<label>.token`. The saved credential lands in
|
|
3832
|
-
* `~/.lattice/db-credentials.enc` keyed by `label`. Caller is
|
|
3833
|
-
* responsible for rewriting the YAML `db:` line to
|
|
3834
|
-
* `${LATTICE_DB:<label>}` and reopening the active Lattice.
|
|
3835
|
-
*
|
|
3836
|
-
* Returns the probe result + (if redeemed) the member info. Throws
|
|
3837
|
-
* if the target is unreachable, or if it's a teams DB and the
|
|
3838
|
-
* caller omitted `invite_token`.
|
|
3839
|
-
*/
|
|
3840
|
-
connectToExistingCloud(opts: {
|
|
3841
|
-
label: string;
|
|
3842
|
-
cloudUrl: string;
|
|
3843
|
-
invite_token?: string;
|
|
3844
|
-
email?: string;
|
|
3845
|
-
name?: string;
|
|
3846
|
-
}): Promise<{
|
|
3847
|
-
probe: CloudProbeResult;
|
|
3848
|
-
joinedAsMember?: {
|
|
3849
|
-
user_id: string;
|
|
3850
|
-
team_id: string;
|
|
3851
|
-
};
|
|
3852
|
-
}>;
|
|
3853
|
-
/**
|
|
3854
|
-
* Initialize a fresh cloud DB's owner: register the first member (who
|
|
3855
|
-
* becomes owner) so the cloud's members + per-table sharing surface
|
|
3856
|
-
* exists. This is NOT a "convert a cloud into a team" step — a cloud
|
|
3857
|
-
* workspace IS a workspace with members; this just bootstraps the owner
|
|
3858
|
-
* the first time a cloud is opened. The hosted server path is the only
|
|
3859
|
-
* supported one:
|
|
3860
|
-
*
|
|
3861
|
-
* - `http(s)://…` — POST to the cloud's `/api/auth/register` endpoint
|
|
3862
|
-
* (a hosted `lattice serve` teams server is fronting the Postgres).
|
|
3863
|
-
* - `postgres(ql)://…` — rejected: direct postgres:// owner bootstrap
|
|
3864
|
-
* is deprecated. Row-level security is enforced by the hosted server,
|
|
3865
|
-
* so it is the only supported connection method for new workspaces.
|
|
3866
|
-
*
|
|
3867
|
-
* On success writes the bearer token to `~/.lattice/keys/<label>.token`
|
|
3868
|
-
* **and** persists the local `__lattice_team_connections` row so the
|
|
3869
|
-
* GUI's team-management API calls can authenticate immediately
|
|
3870
|
-
* afterward (members, invites, kick, destroy). v1.13.4 added the
|
|
3871
|
-
* connection-row write — the older v1.13 implementation only wrote
|
|
3872
|
-
* the token file, leaving GUI authenticated calls with no
|
|
3873
|
-
* `cloud_url` + `my_user_id` + `api_token_encrypted` row to read.
|
|
3874
|
-
*/
|
|
3875
|
-
registerCloudOwner(opts: {
|
|
3876
|
-
label: string;
|
|
3877
|
-
cloudUrl: string;
|
|
3878
|
-
/** Workspace display name (stored as `team_name` for backward compatibility). */
|
|
3879
|
-
teamName: string;
|
|
3880
|
-
email: string;
|
|
3881
|
-
displayName: string;
|
|
3882
|
-
}): Promise<RegisterResponse>;
|
|
3883
|
-
/**
|
|
3884
|
-
* Idempotently initialize a cloud Postgres DB as a collaborative cloud
|
|
3885
|
-
* workspace (members + sharing). 1.16.3 deprecated the user-facing "team"
|
|
3886
|
-
* concept and the explicit "upgrade to team" step — every cloud workspace
|
|
3887
|
-
* gets this machinery automatically at migrate / connect / open time, so the
|
|
3888
|
-
* members + per-table sharing surface is always available on a cloud DB.
|
|
3889
|
-
*
|
|
3890
|
-
* No-op (returns created:false) when the cloud already carries an identity.
|
|
3891
|
-
* On a fresh cloud the caller becomes the owner. Race-safe: a concurrent
|
|
3892
|
-
* initializer that wins the singleton insert is treated as success.
|
|
3893
|
-
*/
|
|
3894
|
-
ensureCloudWorkspaceIdentity(opts: {
|
|
3895
|
-
label: string;
|
|
3896
|
-
cloudUrl: string;
|
|
3897
|
-
workspaceName: string;
|
|
3898
|
-
email: string;
|
|
3899
|
-
displayName?: string;
|
|
3900
|
-
}): Promise<{
|
|
3901
|
-
created: boolean;
|
|
3902
|
-
}>;
|
|
3903
|
-
/** Destroy the singleton team. Creator-only on the cloud side. */
|
|
3904
|
-
destroyTeam(cloudUrl: string, token: string): Promise<void>;
|
|
3905
|
-
listMembers(cloudUrl: string, token: string, teamId: string): Promise<MemberSummary[]>;
|
|
3906
|
-
listPendingInvitations(cloudUrl: string, token: string, teamId: string): Promise<PendingInvitationSummary[]>;
|
|
3907
|
-
invite(cloudUrl: string, token: string, teamId: string, inviteeEmail: string, expiresInHours?: number, inviterUserId?: string): Promise<InviteResponse>;
|
|
3908
|
-
kickMember(cloudUrl: string, token: string, teamId: string, userId: string): Promise<void>;
|
|
3909
|
-
me(cloudUrl: string, token: string): Promise<{
|
|
3910
|
-
user: {
|
|
3911
|
-
id: string;
|
|
3912
|
-
email: string | null;
|
|
3913
|
-
name: string | null;
|
|
3914
|
-
};
|
|
3915
|
-
}>;
|
|
3916
|
-
/**
|
|
3917
|
-
* Share a table with the team. Re-sharing the same table bumps its
|
|
3918
|
-
* `schema_version` and replaces the stored spec — useful for evolving
|
|
3919
|
-
* shared schemas additively.
|
|
3920
|
-
*/
|
|
3921
|
-
shareObject(cloudUrl: string, token: string, teamId: string, table: string, schemaSpec: SchemaSpec, inviterUserId?: string): Promise<ShareObjectResponse>;
|
|
3922
|
-
/**
|
|
3923
|
-
* Stop sharing a table. Only the original sharer or the team creator
|
|
3924
|
-
* can call this. The cloud soft-deletes the `__lattice_shared_objects`
|
|
3925
|
-
* row and appends an `unshare` envelope to the change log.
|
|
3926
|
-
*/
|
|
3927
|
-
unshareObject(cloudUrl: string, token: string, teamId: string, table: string): Promise<void>;
|
|
3928
|
-
listSharedObjects(cloudUrl: string, token: string, teamId: string): Promise<SharedObjectSummary[]>;
|
|
3929
|
-
/**
|
|
3930
|
-
* Pull change envelopes since the given sequence number. Phase 3
|
|
3931
|
-
* emits `schema` and `unshare` ops; Phase 4 adds row-level ops. The
|
|
3932
|
-
* `has_more` flag tells callers to loop until drained before sleeping.
|
|
3933
|
-
*/
|
|
3934
|
-
fetchChangeBatch(cloudUrl: string, token: string, teamId: string, since?: number, limit?: number): Promise<{
|
|
3935
|
-
envelopes: ChangeEnvelope[];
|
|
3936
|
-
has_more: boolean;
|
|
3937
|
-
}>;
|
|
3938
|
-
/**
|
|
3939
|
-
* Fetch every shared object on the team's cloud and apply each spec
|
|
3940
|
-
* to the local lattice. New tables go through `defineLate`; existing
|
|
3941
|
-
* tables get additive ALTER TABLE for any cloud-only columns. PK
|
|
3942
|
-
* mismatches are surfaced as `TeamsSchemaConflictError`s and recorded
|
|
3943
|
-
* in the returned summary so callers can present them to the user.
|
|
3944
|
-
*
|
|
3945
|
-
* Phase 4 will wrap this in a polling loop; Phase 3 invokes it on
|
|
3946
|
-
* demand from the CLI / GUI.
|
|
3947
|
-
*/
|
|
3948
|
-
syncSharedSchemas(connection: TeamConnection): Promise<SyncSharedSchemasResult>;
|
|
3949
|
-
/**
|
|
3950
|
-
* Apply a single cloud SchemaSpec to the local lattice. Returns true
|
|
3951
|
-
* if any change was made (new table, new column), false if local was
|
|
3952
|
-
* already in sync. Throws `TeamsSchemaConflictError` on PK mismatch.
|
|
3953
|
-
*/
|
|
3954
|
-
applyCloudSchemaLocally(table: string, spec: SchemaSpec): Promise<boolean>;
|
|
3955
|
-
/**
|
|
3956
|
-
* Persist a team connection in the local lattice. If a row already
|
|
3957
|
-
* exists for the same `team_id`, it's overwritten — the caller has
|
|
3958
|
-
* presumably just redeemed a fresh invitation or recreated the team.
|
|
3959
|
-
*
|
|
3960
|
-
* Token encryption: Phase 2 stores tokens in plaintext. Lattice's
|
|
3961
|
-
* existing AES-256-GCM encryption layer can wrap this column when the
|
|
3962
|
-
* caller configures `encryptionKey`, but the entity-context encryption
|
|
3963
|
-
* API doesn't apply to raw `define()`/`defineLate()` tables yet. A
|
|
3964
|
-
* follow-up will enable per-column encryption for these internal
|
|
3965
|
-
* tables; until then operators should keep their lattice DB file safe.
|
|
3966
|
-
*/
|
|
3967
|
-
saveConnection(conn: {
|
|
3968
|
-
team_id: string;
|
|
3969
|
-
team_name: string;
|
|
3970
|
-
cloud_url: string;
|
|
3971
|
-
my_user_id: string;
|
|
3972
|
-
api_token: string;
|
|
3973
|
-
}): Promise<void>;
|
|
3974
|
-
deleteConnection(teamId: string): Promise<void>;
|
|
3975
|
-
listConnections(): Promise<TeamConnection[]>;
|
|
3976
|
-
/**
|
|
3977
|
-
* Resolve a team name to a local connection row. Throws if more than
|
|
3978
|
-
* one matches (e.g. user joined two teams with the same name on
|
|
3979
|
-
* different clouds) — caller must disambiguate with team_id.
|
|
3980
|
-
*/
|
|
3981
|
-
findConnectionByName(teamName: string): Promise<TeamConnection | null>;
|
|
3982
|
-
/**
|
|
3983
|
-
* Link a local row to a team. Reads the current local row, POSTs the
|
|
3984
|
-
* snapshot to the cloud (which creates a `__lattice_row_links` row +
|
|
3985
|
-
* mirrors the data into the team's shared table + emits link/upsert
|
|
3986
|
-
* envelopes), and records the link on the local side.
|
|
3987
|
-
*
|
|
3988
|
-
* Also ensures the sync write-hook is attached for `table` so future
|
|
3989
|
-
* local writes to this row drain through the outbox to the cloud.
|
|
3990
|
-
*/
|
|
3991
|
-
linkRow(connection: TeamConnection, table: string, pk: string): Promise<{
|
|
3992
|
-
owner_user_id: string;
|
|
3993
|
-
seq: number;
|
|
3994
|
-
}>;
|
|
3995
|
-
/**
|
|
3996
|
-
* Remove a row from the team. The cloud verifies the caller is the
|
|
3997
|
-
* owner (or team creator) and emits an `unlink` envelope; the local
|
|
3998
|
-
* link row is removed and the local data row is kept in place (Phase
|
|
3999
|
-
* 4 v1 default — receivers' pullers handle their own removal).
|
|
4000
|
-
*/
|
|
4001
|
-
unlinkRow(connection: TeamConnection, table: string, pk: string): Promise<void>;
|
|
4002
|
-
/**
|
|
4003
|
-
* Scan `__lattice_local_links` for tables that currently have at
|
|
4004
|
-
* least one link and ensure a write-hook is registered for each.
|
|
4005
|
-
* Called by CLI sync commands at session start to re-arm hooks after
|
|
4006
|
-
* a process restart (write hooks are bound to the in-memory Lattice
|
|
4007
|
-
* instance, not the underlying DB).
|
|
4008
|
-
*/
|
|
4009
|
-
attachWriteHooks(): Promise<void>;
|
|
4010
|
-
/**
|
|
4011
|
-
* Register a sync write-hook for a single table. Idempotent: a second
|
|
4012
|
-
* call for the same table is a no-op. Called automatically by
|
|
4013
|
-
* `linkRow` and `applyCloudSchemaLocally`; manual callers can invoke
|
|
4014
|
-
* directly for tables that exist before any link/share happens.
|
|
4015
|
-
*
|
|
4016
|
-
* The hook captures local writes to linked rows into
|
|
4017
|
-
* `__lattice_team_outbox`. The replay guard (`_isReplaying`) skips
|
|
4018
|
-
* captures during envelope application so pulled changes don't get
|
|
4019
|
-
* pushed back to the cloud.
|
|
4020
|
-
*/
|
|
4021
|
-
ensureWriteHook(table: string): void;
|
|
4022
|
-
private captureWrite;
|
|
4023
|
-
/**
|
|
4024
|
-
* Drain the outbox for one team in FIFO order. Each entry POSTs to
|
|
4025
|
-
* the cloud's row endpoint (or DELETEs for soft-delete ops). 2xx
|
|
4026
|
-
* deletes the outbox row; failures increment `attempts` + set
|
|
4027
|
-
* `next_attempt_at` to an exponential-backoff future timestamp,
|
|
4028
|
-
* leaving the row for a later drain.
|
|
4029
|
-
*
|
|
4030
|
-
* Phase 4 v1: no separate dead-letter for outbox entries. Repeated
|
|
4031
|
-
* 4xx responses just keep retrying with a growing backoff — operator
|
|
4032
|
-
* surfaces them via `lattice teams status`.
|
|
4033
|
-
*/
|
|
4034
|
-
drainOutbox(connection: TeamConnection): Promise<PushResult>;
|
|
4035
|
-
/**
|
|
4036
|
-
* Pull change envelopes from the cloud and apply them to the local
|
|
4037
|
-
* lattice. Loops internally until the cloud reports no more pending
|
|
4038
|
-
* envelopes, so a single call drains arbitrary backlog. Bookkeeping:
|
|
4039
|
-
* the local connection's `last_change_seq` advances per successful
|
|
4040
|
-
* envelope; the replay guard prevents pulled writes from being re-
|
|
4041
|
-
* pushed back to the cloud via the outbox.
|
|
4042
|
-
*
|
|
4043
|
-
* Individual envelope failures land in `__lattice_team_dlq` so one
|
|
4044
|
-
* bad row doesn't stall the stream.
|
|
4045
|
-
*/
|
|
4046
|
-
pullChanges(connection: TeamConnection, batchSize?: number): Promise<PullResult>;
|
|
4047
|
-
private applyEnvelope;
|
|
4048
|
-
/**
|
|
4049
|
-
* Apply an `upsert` envelope to a local row, guarding against silently
|
|
4050
|
-
* clobbering a non-owner's local edit.
|
|
4051
|
-
*
|
|
4052
|
-
* A non-owner who edits a mirrored row locally produces no outbox entry
|
|
4053
|
-
* (only owners push), so the next owner update would overwrite it with no
|
|
4054
|
-
* trace. Before the last-write-wins overwrite, this compares the current
|
|
4055
|
-
* local row against the hash captured at the last sync (`synced_hash` on
|
|
4056
|
-
* the link row): if they differ, the local copy diverged, so we record a
|
|
4057
|
-
* `divergence` entry in the DLQ — capturing the lost local content — then
|
|
4058
|
-
* still apply (LWW keeps sync converging). The loss is now visible via
|
|
4059
|
-
* `lattice teams dlq list` instead of being silent.
|
|
4060
|
-
*
|
|
4061
|
-
* Owner rows are never overwritten by foreign upserts, so they skip the
|
|
4062
|
-
* check. `synced_hash` is (re)stamped from the stored row after every apply.
|
|
4063
|
-
*/
|
|
4064
|
-
private applyUpsertEnvelope;
|
|
4065
|
-
private findLocalLink;
|
|
4066
|
-
/**
|
|
4067
|
-
* List the DLQ entries for a team, newest first, with the stored envelope
|
|
4068
|
-
* parsed for inspection.
|
|
4069
|
-
*/
|
|
4070
|
-
listDlq(connection: TeamConnection): Promise<DlqEntry[]>;
|
|
4071
|
-
/**
|
|
4072
|
-
* Replay DLQ entries through {@link applyEnvelope}. With `id`, retries just
|
|
4073
|
-
* that entry; otherwise retries every entry for the team (oldest first, so
|
|
4074
|
-
* dependencies replay before dependents). An entry that applies cleanly is
|
|
4075
|
-
* deleted; one that fails again stays, with its `error` refreshed. Runs
|
|
4076
|
-
* under the replay guard so a re-applied row isn't pushed back to the cloud.
|
|
4077
|
-
*/
|
|
4078
|
-
retryDlq(connection: TeamConnection, id?: string): Promise<DlqRetryResult>;
|
|
4079
|
-
/**
|
|
4080
|
-
* Delete DLQ entries without applying them. With `id`, purges just that
|
|
4081
|
-
* entry; otherwise purges every entry for the team. Returns the count
|
|
4082
|
-
* removed. Use to discard divergence notices or envelopes that will never
|
|
4083
|
-
* apply.
|
|
4084
|
-
*/
|
|
4085
|
-
purgeDlq(connection: TeamConnection, id?: string): Promise<number>;
|
|
4086
|
-
getStatus(connection: TeamConnection): Promise<SyncStatus>;
|
|
4087
|
-
private fetchUnauthed;
|
|
4088
|
-
private fetchAuthed;
|
|
4089
|
-
}
|
|
4090
|
-
declare class TeamsHttpError extends Error {
|
|
4091
|
-
readonly status: number;
|
|
4092
|
-
constructor(status: number, message: string);
|
|
4093
|
-
}
|
|
4094
|
-
|
|
4095
3765
|
/**
|
|
4096
3766
|
* Cloud migration — copy a Lattice's data from one backing store to
|
|
4097
3767
|
* another. Used by the GUI's "Migrate to cloud" flow, but exported as
|
|
@@ -4172,58 +3842,350 @@ declare function openTargetLatticeForMigration(configPath: string, targetUrl: st
|
|
|
4172
3842
|
declare function archiveLocalSqlite(dbPath: string): string;
|
|
4173
3843
|
|
|
4174
3844
|
/**
|
|
4175
|
-
*
|
|
4176
|
-
*
|
|
4177
|
-
*
|
|
3845
|
+
* Cloud-connect probe — non-destructive inspection of a candidate target
|
|
3846
|
+
* Lattice (a Postgres URL the user wants to migrate to or join). Used by the
|
|
3847
|
+
* GUI's "Migrate to cloud" and "Join a cloud" flows to tell, before the user
|
|
3848
|
+
* commits, whether a URL points at a fresh database or an already-secured
|
|
3849
|
+
* Lattice cloud. Exported as public API so library consumers can pre-flight the
|
|
3850
|
+
* same check.
|
|
3851
|
+
*
|
|
3852
|
+
* A "cloud" in v3 is a shared Postgres database with Lattice RLS installed —
|
|
3853
|
+
* its tell is the bookkeeping table `__lattice_owners` (created by
|
|
3854
|
+
* `installCloudRls`). The probe opens the URL with `introspectOnly` (NO DDL, so
|
|
3855
|
+
* it works even when the caller holds a scoped, non-superuser member role), asks
|
|
3856
|
+
* Postgres whether that table exists, and closes. It never mutates the target.
|
|
4178
3857
|
*/
|
|
4179
|
-
interface
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
3858
|
+
interface CloudProbeResult {
|
|
3859
|
+
/** True iff the URL could be opened (connected + authenticated). */
|
|
3860
|
+
reachable: boolean;
|
|
3861
|
+
/** Adapter dialect of the probed URL. */
|
|
3862
|
+
dialect: 'sqlite' | 'postgres';
|
|
3863
|
+
/**
|
|
3864
|
+
* True iff the target is an established Lattice cloud — Postgres with RLS
|
|
3865
|
+
* bookkeeping (`__lattice_owners`) present. A fresh Postgres (no Lattice
|
|
3866
|
+
* schema yet) and any SQLite file are `false`: the former is a migration
|
|
3867
|
+
* target, the latter is a private local store.
|
|
3868
|
+
*/
|
|
3869
|
+
isCloud: boolean;
|
|
3870
|
+
/** Underlying error message when `reachable: false`. */
|
|
3871
|
+
error?: string;
|
|
4191
3872
|
}
|
|
3873
|
+
/** Detect the RLS bookkeeping table without assuming SELECT privilege on it.
|
|
3874
|
+
* `to_regclass` returns the table's OID name when it exists and NULL when it
|
|
3875
|
+
* doesn't — and, unlike `SELECT FROM __lattice_owners`, it does not require any
|
|
3876
|
+
* privilege on the table, so a scoped member (who is denied SELECT on the
|
|
3877
|
+
* bookkeeping tables) still gets a truthful answer. */
|
|
3878
|
+
declare function cloudRlsInstalled(probe: Lattice): Promise<boolean>;
|
|
3879
|
+
/**
|
|
3880
|
+
* Whether the connected role may create other roles — the capability that
|
|
3881
|
+
* separates a cloud OWNER (ran the migration, owns the rows, can invite members)
|
|
3882
|
+
* from a scoped MEMBER (provisioned `NOCREATEROLE`). Read from
|
|
3883
|
+
* `pg_roles.rolcreaterole` for the live role. SQLite or any error → false.
|
|
3884
|
+
*/
|
|
3885
|
+
declare function canManageRoles(db: Lattice): Promise<boolean>;
|
|
3886
|
+
/**
|
|
3887
|
+
* Probe a candidate Lattice URL for reachability + cloud status.
|
|
3888
|
+
*
|
|
3889
|
+
* Never throws. Errors are returned in the result's `error` field with
|
|
3890
|
+
* `reachable: false`.
|
|
3891
|
+
*/
|
|
3892
|
+
declare function probeCloud(targetUrl: string): Promise<CloudProbeResult>;
|
|
3893
|
+
|
|
4192
3894
|
/**
|
|
4193
|
-
* True iff `url` parses as a `postgres://` / `postgresql://` URL
|
|
4194
|
-
*
|
|
4195
|
-
*
|
|
3895
|
+
* True iff `url` parses as a `postgres://` / `postgresql://` URL. Used by
|
|
3896
|
+
* the GUI to distinguish a cloud (shared Postgres) connection from a local
|
|
3897
|
+
* SQLite file path.
|
|
4196
3898
|
*/
|
|
4197
3899
|
declare function isPostgresUrl(url: string): boolean;
|
|
3900
|
+
|
|
4198
3901
|
/**
|
|
4199
|
-
*
|
|
3902
|
+
* One-time bootstrap for a cloud: the ownership bookkeeping tables and the shared
|
|
3903
|
+
* `SECURITY DEFINER` helpers. Idempotent (`CREATE TABLE IF NOT EXISTS`,
|
|
3904
|
+
* `CREATE OR REPLACE FUNCTION`). Multi-statement — Postgres-only, so it never hits
|
|
3905
|
+
* the single-statement SQLite migration path.
|
|
4200
3906
|
*
|
|
4201
|
-
*
|
|
4202
|
-
*
|
|
4203
|
-
*
|
|
4204
|
-
*
|
|
4205
|
-
|
|
4206
|
-
|
|
3907
|
+
* NOTE (follow-up): the `SECURITY DEFINER` helpers below should pin `search_path`
|
|
3908
|
+
* to the cloud schema to fully close the definer-search_path class of issue. Today
|
|
3909
|
+
* members are `NOSUPERUSER` without CREATE on the schema, so they cannot plant a
|
|
3910
|
+
* shadowing object; the pin is hardening, tracked for the schema-awareness pass.
|
|
3911
|
+
*/
|
|
3912
|
+
/**
|
|
3913
|
+
* Group role every cloud member inherits. Table privileges are granted to the
|
|
3914
|
+
* group, so adding a shared table or a member is a single GRANT — while RLS still
|
|
3915
|
+
* filters rows per individual login role (`session_user`). The group grants
|
|
3916
|
+
* *access*, never *visibility*.
|
|
3917
|
+
*/
|
|
3918
|
+
declare const MEMBER_GROUP = "lattice_members";
|
|
3919
|
+
/** Install the cloud RLS bootstrap (bookkeeping + helper functions). No-op on SQLite. */
|
|
3920
|
+
declare function installCloudRls(db: Lattice): Promise<void>;
|
|
3921
|
+
/**
|
|
3922
|
+
* Secure the observation substrate (`__lattice_changelog`) so a member reads
|
|
3923
|
+
* only what they're allowed to: a DERIVED observation only when it can reach
|
|
3924
|
+
* EVERY source it was derived from (so a hidden enrichment never reaches the
|
|
3925
|
+
* member — existence-hiding is structural), and a ground-truth / audit entry
|
|
3926
|
+
* only for a row that is itself visible to the member. Both predicates route
|
|
3927
|
+
* through the `session_user`-keyed SECURITY DEFINER helpers, so they bind to the
|
|
3928
|
+
* real member. `FORCE ROW LEVEL SECURITY` applies the policy even to the table
|
|
3929
|
+
* owner. No-op on SQLite (single-user; no cross-viewer leak to guard). Run after
|
|
3930
|
+
* the change-log table exists (`Lattice.ensureObservationSubstrate`).
|
|
3931
|
+
*/
|
|
3932
|
+
declare function enableChangelogRls(db: Lattice): Promise<void>;
|
|
3933
|
+
/** Enable RLS on one shared table. No-op on SQLite. Idempotent via a per-table version key. */
|
|
3934
|
+
declare function enableRlsForTable(db: Lattice, table: string, pkCols: readonly string[]): Promise<void>;
|
|
3935
|
+
/**
|
|
3936
|
+
* Stamp the current role as owner of every row that already exists in a table —
|
|
3937
|
+
* for data migrated into a cloud BEFORE the ownership trigger existed (the
|
|
3938
|
+
* trigger only fires on new writes). Without this, migrated rows have no
|
|
3939
|
+
* ownership record and RLS would hide them from everyone. Idempotent; no-op on
|
|
3940
|
+
* SQLite or an unkeyable table.
|
|
3941
|
+
*/
|
|
3942
|
+
declare function backfillOwnership(db: Lattice, table: string, pkCols: readonly string[]): Promise<void>;
|
|
3943
|
+
|
|
3944
|
+
/** A URL-safe random password (48 hex chars) for a new member role. */
|
|
3945
|
+
declare function generateMemberPassword(): string;
|
|
3946
|
+
/**
|
|
3947
|
+
* Derive a safe, unique Postgres role name from a free-form label (e.g. an email
|
|
3948
|
+
* or display name). Lowercased, non-word chars collapsed to `_`, prefixed so it
|
|
3949
|
+
* always starts legally and namespaced under `lm_`, with a short random suffix so
|
|
3950
|
+
* two people with similar labels never collide.
|
|
3951
|
+
*/
|
|
3952
|
+
declare function memberRoleName(label: string): string;
|
|
3953
|
+
/**
|
|
3954
|
+
* Create (or re-key) a scoped member LOGIN role and add it to the member group.
|
|
3955
|
+
* Idempotent on the role's existence: a re-invite rotates the password. Requires
|
|
3956
|
+
* the connection to hold `CREATEROLE`. After this, the member connects with
|
|
3957
|
+
* `postgres://<role>:<password>@<host>/<db>` and sees only its permitted rows.
|
|
3958
|
+
*/
|
|
3959
|
+
declare function provisionMemberRole(db: Lattice, role: string, password: string): Promise<void>;
|
|
3960
|
+
/**
|
|
3961
|
+
* Change a row's sharing through the owner-only `lattice_set_row_visibility`
|
|
3962
|
+
* SECURITY DEFINER function. Only the row's owner (Postgres raises for anyone
|
|
3963
|
+
* else, enforced inside the function) may call it. `pk` is the row's canonical
|
|
3964
|
+
* primary-key string — a single-column key is the bare value; a composite key is
|
|
3965
|
+
* its columns joined by TAB, matching Lattice's serialization.
|
|
3966
|
+
*/
|
|
3967
|
+
declare function setRowVisibility(db: Lattice, table: string, pk: string, visibility: string): Promise<void>;
|
|
3968
|
+
/**
|
|
3969
|
+
* Per-card audience override: grant (or revoke) one member access to ONE masked
|
|
3970
|
+
* cell — a specific (table, pk, column) — without changing the column's
|
|
3971
|
+
* schema-level audience. Owner-only (the SQL function raises for a non-owner).
|
|
3972
|
+
* `pk` is the row's canonical primary-key string.
|
|
3973
|
+
*/
|
|
3974
|
+
declare function grantCell(db: Lattice, table: string, pk: string, column: string, grantee: string): Promise<void>;
|
|
3975
|
+
declare function revokeCell(db: Lattice, table: string, pk: string, column: string, grantee: string): Promise<void>;
|
|
3976
|
+
/**
|
|
3977
|
+
* Remove a member: clear its privileges and drop the role. NOTE: rows the member
|
|
3978
|
+
* owned remain in their tables but become unreachable (their `owner_role` no
|
|
3979
|
+
* longer matches any login role, and RLS shows a row only to its owner / grantees
|
|
3980
|
+
* / everyone) — reassigning or purging a departed member's rows is a separate,
|
|
3981
|
+
* deliberate step, not a side effect of revoking access.
|
|
3982
|
+
*/
|
|
3983
|
+
declare function revokeMemberRole(db: Lattice, role: string): Promise<void>;
|
|
3984
|
+
|
|
3985
|
+
/**
|
|
3986
|
+
* Physical-schema discovery for cloud members. A member connects to a shared
|
|
3987
|
+
* cloud as a scoped role whose local config may declare NO entities — yet the
|
|
3988
|
+
* GUI must show every table the member is allowed to use. Postgres only exposes
|
|
3989
|
+
* a table in `pg_tables` / `information_schema` to a role that holds a privilege
|
|
3990
|
+
* on it, so listing the role's visible user tables is exactly the set RLS lets
|
|
3991
|
+
* it touch: the member's granted tables, never another's bookkeeping.
|
|
3992
|
+
*/
|
|
3993
|
+
interface DiscoveredTable {
|
|
3994
|
+
name: string;
|
|
3995
|
+
columns: string[];
|
|
3996
|
+
/** Primary-key column(s), in key order. May be empty for a keyless table. */
|
|
3997
|
+
pk: string[];
|
|
3998
|
+
}
|
|
3999
|
+
/**
|
|
4000
|
+
* List the user tables a member's role is actually privileged to use, excluding
|
|
4001
|
+
* Lattice/GUI bookkeeping (anything starting `_`). `information_schema.tables`
|
|
4002
|
+
* is privilege-filtered — it shows a role only the tables it holds a grant on or
|
|
4003
|
+
* owns — so this returns exactly the member's reachable set, never another
|
|
4004
|
+
* member's bookkeeping (which is granted to no one). Scoped to `current_schema()`
|
|
4005
|
+
* so it follows the connection's search_path (the cloud's `public` in production).
|
|
4006
|
+
* Returns each table's columns + primary key so the caller can register it.
|
|
4007
|
+
* Postgres-only — returns `[]` on SQLite (a private, single-user store).
|
|
4008
|
+
*/
|
|
4009
|
+
declare function discoverCloudTables(db: Lattice): Promise<DiscoveredTable[]>;
|
|
4010
|
+
|
|
4011
|
+
/** True when this audience means "no mask" (visible to whoever can see the row). */
|
|
4012
|
+
declare function isRowAudience(audience: string | undefined): boolean;
|
|
4013
|
+
/**
|
|
4014
|
+
* Compile a column `audience` spec into a boolean SQL predicate over the helper
|
|
4015
|
+
* functions. Returns `'true'` for the row-audience / everyone case. Throws on an
|
|
4016
|
+
* unknown or malformed clause.
|
|
4017
|
+
*/
|
|
4018
|
+
declare function audiencePredicate(audience: string): string;
|
|
4019
|
+
/** Whether a table needs a masking view at all (any column has a real audience). */
|
|
4020
|
+
declare function tableNeedsAudienceView(columnAudience: Record<string, string>): boolean;
|
|
4021
|
+
/**
|
|
4022
|
+
* SQL to (re)generate a table's cell-masking view, point members at it, and make
|
|
4023
|
+
* the base table's columns unreachable to members so the mask can't be bypassed:
|
|
4207
4024
|
*
|
|
4208
|
-
*
|
|
4209
|
-
*
|
|
4210
|
-
*
|
|
4211
|
-
*
|
|
4212
|
-
*
|
|
4025
|
+
* - `CREATE OR REPLACE VIEW <t>_v` — every column passes through, except
|
|
4026
|
+
* audience columns which become `CASE WHEN <predicate> THEN col END`.
|
|
4027
|
+
* - The view re-applies ROW visibility with `WHERE lattice_row_visible(t, pk)`.
|
|
4028
|
+
* This is essential: the view runs with its OWNER's rights, so the base
|
|
4029
|
+
* table's RLS would be evaluated as the owner (who sees everything). The
|
|
4030
|
+
* `session_user`-keyed SECURITY DEFINER helper re-binds row filtering to the
|
|
4031
|
+
* real member, so an owner-defined view still filters per viewer.
|
|
4032
|
+
* - `GRANT SELECT` on the view + `REVOKE SELECT` on the base from members: a
|
|
4033
|
+
* member reads only the masked, row-filtered view and cannot reach the raw
|
|
4034
|
+
* column. (Member writes to such a table flow through the observation path —
|
|
4035
|
+
* members keep INSERT/UPDATE/DELETE on the base under RLS; only SELECT moves
|
|
4036
|
+
* to the view.)
|
|
4213
4037
|
*
|
|
4214
|
-
*
|
|
4215
|
-
*
|
|
4216
|
-
|
|
4038
|
+
* Idempotent. `columns` is the table's full column list (stable order); `pkCols`
|
|
4039
|
+
* its primary key, so the row filter matches the RLS policy's pk serialization.
|
|
4040
|
+
*/
|
|
4041
|
+
declare function audienceViewSql(table: string, columns: readonly string[], pkCols: readonly string[], columnAudience: Record<string, string>): string;
|
|
4042
|
+
/**
|
|
4043
|
+
* Generate + install a table's cell-masking view (Postgres only; no-op on SQLite
|
|
4044
|
+
* and on a table with no audience columns). Versioned by a content hash of the
|
|
4045
|
+
* columns / pk / column-audience so a changed spec regenerates and an unchanged
|
|
4046
|
+
* one is skipped. Run AFTER the table + RLS exist (the view reuses the row
|
|
4047
|
+
* visibility helper and revokes the base SELECT that enableRlsForTable granted).
|
|
4048
|
+
*/
|
|
4049
|
+
declare function enableAudienceView(db: Lattice, table: string, columns: readonly string[], pkCols: readonly string[], columnAudience: Record<string, string>): Promise<void>;
|
|
4050
|
+
|
|
4051
|
+
/**
|
|
4052
|
+
* The per-viewer fold (the "local compile" of the per-viewer enrichment model).
|
|
4217
4053
|
*
|
|
4218
|
-
*
|
|
4219
|
-
*
|
|
4054
|
+
* Source-gated enrichment is per-viewer: a value derived from a file that one
|
|
4055
|
+
* member can't see must not appear for that member. Postgres RLS + the generated
|
|
4056
|
+
* mask view handle row visibility and fixed-policy columns; this handles the
|
|
4057
|
+
* remaining case — a column whose VALUE differs by which sources you can reach.
|
|
4220
4058
|
*
|
|
4221
|
-
*
|
|
4222
|
-
*
|
|
4223
|
-
*
|
|
4224
|
-
*
|
|
4059
|
+
* The compile is a deterministic, programmatic fold (NOT AI): start from the
|
|
4060
|
+
* broadly-visible ground-truth projection, then replay the observations the
|
|
4061
|
+
* viewer is allowed to see — latest audience-visible observation per attribute
|
|
4062
|
+
* wins. Because observations are additive and only the viewer-visible ones
|
|
4063
|
+
* contribute, the fold is provably leak-free (sound only for additive/monotonic
|
|
4064
|
+
* derivations) and revocation is structural: drop a viewer's access to a source
|
|
4065
|
+
* and every value derived from it silently reverts to the prior visible
|
|
4066
|
+
* observation (or ground truth), with no residue — no promotion, no copy left
|
|
4067
|
+
* behind. Run on the member's local replica over already-audience-gated
|
|
4068
|
+
* observations, so hidden observations never reach them (existence-hiding is
|
|
4069
|
+
* structural) and egress is paid once at pull, never per read.
|
|
4070
|
+
*/
|
|
4071
|
+
/** One attribute-level observation — a single column's value with its provenance. */
|
|
4072
|
+
interface Observation {
|
|
4073
|
+
/** The column this observation sets. */
|
|
4074
|
+
attribute: string;
|
|
4075
|
+
/** The value it sets the column to. */
|
|
4076
|
+
value: unknown;
|
|
4077
|
+
/** Ordering key (ISO timestamp). Latest visible observation per attribute wins. */
|
|
4078
|
+
createdAt: string;
|
|
4079
|
+
/** `ground_truth` (always visible) or `derived` (gated by its source-set). */
|
|
4080
|
+
changeKind?: 'ground_truth' | 'derived' | null;
|
|
4081
|
+
/** Source ids that produced a derived value. The observation is visible to a
|
|
4082
|
+
* viewer only if the viewer can see EVERY one of them (intersection-of-sources
|
|
4083
|
+
* reader set — losing any source hides the derived value). */
|
|
4084
|
+
sourceRef?: readonly string[] | null;
|
|
4085
|
+
}
|
|
4086
|
+
/** What a given member can reach, for deciding observation visibility. */
|
|
4087
|
+
interface Viewer {
|
|
4088
|
+
/** Source ids (file primary keys) this member can currently see. */
|
|
4089
|
+
visibleSources: ReadonlySet<string>;
|
|
4090
|
+
}
|
|
4091
|
+
/**
|
|
4092
|
+
* Whether a viewer may see an observation. Ground-truth is always visible (it is
|
|
4093
|
+
* the broadly-shared projection). A derived observation is visible iff the viewer
|
|
4094
|
+
* can see every source it was derived from — so un-sharing or deleting any one
|
|
4095
|
+
* source drops it. An unsourced derived observation is treated as hidden (fail
|
|
4096
|
+
* closed): a derived value with no recorded provenance can't be proven safe.
|
|
4097
|
+
*/
|
|
4098
|
+
declare function observationVisible(obs: Observation, viewer: Viewer): boolean;
|
|
4099
|
+
/**
|
|
4100
|
+
* Compile one per-viewer entity: overlay the viewer-visible observations onto the
|
|
4101
|
+
* ground-truth projection, latest-per-attribute winning. Pure + deterministic —
|
|
4102
|
+
* the same (ground, observations, viewer) always yields the same row, and an
|
|
4103
|
+
* observation the viewer can't see never affects the result.
|
|
4104
|
+
*/
|
|
4105
|
+
declare function foldEntity(ground: Row, observations: readonly Observation[], viewer: Viewer): Row;
|
|
4106
|
+
/**
|
|
4107
|
+
* Expand a change-log row's `changes` object into per-attribute observations.
|
|
4108
|
+
* The change-log records one row per write with a JSON `changes` map; the fold
|
|
4109
|
+
* works per attribute, so each changed field becomes its own observation carrying
|
|
4110
|
+
* the write's provenance. (Caller supplies the already-parsed change-log entry.)
|
|
4111
|
+
*/
|
|
4112
|
+
declare function observationsFromChange(entry: {
|
|
4113
|
+
changes: Record<string, unknown> | null;
|
|
4114
|
+
createdAt: string;
|
|
4115
|
+
changeKind?: 'ground_truth' | 'derived' | null;
|
|
4116
|
+
sourceRef?: readonly string[] | null;
|
|
4117
|
+
}): Observation[];
|
|
4118
|
+
|
|
4119
|
+
declare class FoldCache {
|
|
4120
|
+
private readonly cache;
|
|
4121
|
+
/** Compiled entity for (rowId, viewer), computing + caching it on a miss. */
|
|
4122
|
+
get(rowId: string, ground: Row, observations: readonly Observation[], viewer: Viewer): Row;
|
|
4123
|
+
/** Drop every cached version of a row — call when a new observation lands for
|
|
4124
|
+
* it (any viewer's compile may have changed). Cheap, exact, no over-eviction
|
|
4125
|
+
* of other rows. */
|
|
4126
|
+
invalidateRow(rowId: string): void;
|
|
4127
|
+
/** Drop everything (e.g. on a full replica re-pull). */
|
|
4128
|
+
clear(): void;
|
|
4129
|
+
/** Number of cached (row, viewer) compilations — for tests / introspection. */
|
|
4130
|
+
get size(): number;
|
|
4131
|
+
}
|
|
4132
|
+
|
|
4133
|
+
/**
|
|
4134
|
+
* Turn a Postgres database into a secured Lattice cloud, in place: install the
|
|
4135
|
+
* RLS bootstrap + the observation substrate, then for every registered user
|
|
4136
|
+
* table stamp the current role as owner of the existing rows and force RLS (plus
|
|
4137
|
+
* a cell-masking view for any audience columns). Idempotent and additive — safe
|
|
4138
|
+
* to run on a fresh migration target OR on an already-populated Postgres that
|
|
4139
|
+
* isn't a cloud yet (the "secure this cloud" cutover). No-op on SQLite.
|
|
4140
|
+
*
|
|
4141
|
+
* Must run as a role that owns the tables and can create roles (a cloud
|
|
4142
|
+
* owner / DBA). `backfillOwnership` runs BEFORE `enableRlsForTable` so a
|
|
4143
|
+
* non-superuser owner can still SELECT every row to stamp it before FORCE RLS
|
|
4144
|
+
* filters the table to rows it already owns.
|
|
4145
|
+
*/
|
|
4146
|
+
declare function secureCloud(db: Lattice): Promise<void>;
|
|
4147
|
+
|
|
4148
|
+
/**
|
|
4149
|
+
* Workspace-level settings for a cloud — cloud-wide values the OWNER controls and
|
|
4150
|
+
* members never see in the product surface. Stored in `__lattice_cloud_settings`,
|
|
4151
|
+
* a bookkeeping table members have no grant on — so its VALUE is unreadable to a
|
|
4152
|
+
* member (SELECT is denied, like every other `__lattice_*` table; the System view
|
|
4153
|
+
* may still list the table's existence + column names from the catalog, but never
|
|
4154
|
+
* its contents). It is reached only through two `SECURITY DEFINER` helpers:
|
|
4155
|
+
*
|
|
4156
|
+
* - `lattice_get_cloud_setting(key)` — readable by members, because a member's
|
|
4157
|
+
* own chat must inject the value (the chat call is assembled in each member's
|
|
4158
|
+
* LOCAL gui process). This is the deliberate, documented ceiling: secrecy is
|
|
4159
|
+
* **app-mediated** (hidden from the UI + every API response), NOT cryptographic
|
|
4160
|
+
* — a member CAN read the value from their own session if they go looking.
|
|
4161
|
+
* - `lattice_set_cloud_setting(key, value)` — owner-only (RAISEs unless the
|
|
4162
|
+
* caller can create roles, the same gate as `lattice_assign_role`).
|
|
4163
|
+
*
|
|
4164
|
+
* Postgres-only: a local SQLite workspace is single-user, so there is nothing to
|
|
4165
|
+
* keep secret and these are all no-ops / null there.
|
|
4166
|
+
*/
|
|
4167
|
+
/** Setting key for the chat system prompt an owner bundles into every member's chat. */
|
|
4168
|
+
declare const CLOUD_SETTING_SYSTEM_PROMPT = "chat_system_prompt";
|
|
4169
|
+
/**
|
|
4170
|
+
* Install the workspace-settings table + helpers. Idempotent (`CREATE TABLE IF
|
|
4171
|
+
* NOT EXISTS` / `CREATE OR REPLACE FUNCTION`). No-op on SQLite. Run as the cloud
|
|
4172
|
+
* owner — `secureCloud` calls it for new clouds, and the owner-only settings
|
|
4173
|
+
* endpoint calls it lazily so an already-secured cloud picks it up on first use.
|
|
4174
|
+
*/
|
|
4175
|
+
declare function installCloudSettings(db: Lattice): Promise<void>;
|
|
4176
|
+
/**
|
|
4177
|
+
* Read a cloud workspace setting via the SECURITY DEFINER getter. Best-effort:
|
|
4178
|
+
* returns null on SQLite, on a cloud that hasn't installed the helper yet (the
|
|
4179
|
+
* function is absent), or on any error — so a caller treats "unset" and "couldn't
|
|
4180
|
+
* read" identically (e.g. the chat path simply injects nothing).
|
|
4181
|
+
*/
|
|
4182
|
+
declare function getCloudSetting(db: Lattice, key: string): Promise<string | null>;
|
|
4183
|
+
/**
|
|
4184
|
+
* Owner-only: write a cloud workspace setting via the SECURITY DEFINER setter.
|
|
4185
|
+
* The function RAISEs if the caller isn't a cloud owner; that surfaces here as a
|
|
4186
|
+
* thrown error which the (already owner-gated) endpoint reports. Not silent.
|
|
4225
4187
|
*/
|
|
4226
|
-
declare function
|
|
4188
|
+
declare function setCloudSetting(db: Lattice, key: string, value: string): Promise<void>;
|
|
4227
4189
|
|
|
4228
4190
|
/** A content block in the Anthropic message format used here. */
|
|
4229
4191
|
type ContentBlock = {
|
|
@@ -4514,4 +4476,4 @@ interface PdfOptions {
|
|
|
4514
4476
|
*/
|
|
4515
4477
|
declare function describePdf(auth: ClaudeAuth, path: string, opts?: PdfOptions): Promise<string>;
|
|
4516
4478
|
|
|
4517
|
-
export { type AddWorkspaceOptions, type AdoptNativeOptions, type AdoptResult, type ApplyWriteResult, type AuditEvent, type AutoUpdateResult, type BelongsToRelation, type BelongsToSource, type BlobMetadata, type BuiltinTemplateName, CONFIG_SUBDIR, type CatalogEntity, type CatalogRecord, type ChangeEntry, type ChangelogOptions, type ClassifyMatch, type CleanupOptions, type CleanupResult, type CloudProbeResult, type CountOptions, type CrawlOptions, type CrawlResult, type CustomSource, DEFAULT_ENTRY_TYPES, DEFAULT_TYPE_ALIASES, type
|
|
4479
|
+
export { type AddWorkspaceOptions, type AdoptNativeOptions, type AdoptResult, type ApplyWriteResult, type AuditEvent, type AutoUpdateResult, type BelongsToRelation, type BelongsToSource, type BlobMetadata, type BuiltinTemplateName, CLOUD_SETTING_SYSTEM_PROMPT, CONFIG_SUBDIR, type CatalogEntity, type CatalogRecord, type ChangeEntry, type ChangelogOptions, type ClassifyMatch, type CleanupOptions, type CleanupResult, type CloudProbeResult, type CountOptions, type CrawlOptions, type CrawlResult, type CustomSource, DEFAULT_ENTRY_TYPES, DEFAULT_TYPE_ALIASES, type DiscoveredTable, type EmbeddingsConfig, type EnrichOptions, type EnrichResult, type EnrichedSource, type EnrichmentLookup, type EntityContextDefinition, type EntityContextManifestEntry, type EntityFileManifestInfo, type EntityFileSource, type EntityFileSpec, type EntityProfileField, type EntityProfileSection, type EntityProfileTemplate, type EntityRenderSpec, type EntityRenderTemplate, type EntitySectionPerRow, type EntitySectionsTemplate, type EntityTableColumn, type EntityTableTemplate, type ExtractedObject, type FilesRow, type Filter, type FilterOp, FoldCache, type FtsConfig, type FtsGroup, type FtsHit, type FtsOptions, type FtsResult, type HasManyRelation, type HasManySource, InMemorySourceKeyStore, InMemoryStateStore, type InitOptions, LOCAL_DB_RELPATH, Lattice, type LatticeConfig, type LatticeConfigInput, type LatticeEntityDef, type LatticeEntityRenderSpec, type LatticeFieldDef, type LatticeFieldType, type LatticeManifest, type LatticeOptions, type LinkOptions, type LlmClient, type LlmMessage, MEMBER_GROUP, type ManyToManySource, type MarkdownTableColumn, type MigrateResult, type Migration, type MigrationOptions, type MigrationProgress, type MigrationResult, type MultiTableDefinition, NATIVE_ENTITY_DEFS, NATIVE_ENTITY_NAMES, NATIVE_REGISTRY_TABLE, type Observation, type OrderBySpec, type OrganizeOptions, type OrganizeResult, type OrganizedCreation, type OrganizedLink, type ParseError, type ParseResult, type ParsedConfig, type PdfOptions, type PdfSenderInput, type PkLookup, PostgresAdapter, type PostgresAdapterOptions, type PreparedStatement, type PrimaryKey, type QueryOptions, READ_ONLY_HEADER, ROOT_DIRNAME, type ReadOnlyHeaderOptions, type ReconcileOptions, type ReconcileResult, type RefKind, type RefProvider, type ReferenceMetadata, ReferenceUnavailableError, type Relation, type RemoteBlobStore, type RenderHooks, type RenderResult, type RenderSpec, type ReportConfig, type ReportResult, type ReportSection, type ReportSectionResult, type ResolveOptions, type ReverseSeedDetection, type ReverseSeedResult, type ReverseSeedTableResult, type ReverseSyncError, type ReverseSyncResult, type ReverseSyncUpdate, type RewardScores, type Row, type S3Config, type S3StoreConfig, S3UnavailableError, SQLiteAdapter, type SchemaEntity, type SearchOptions, type SearchResult, type SecurityOptions, type SeedConfig, type SeedLinkSpec, SeedReconciliationError, type SeedResult, type SelfSource, type SessionEntry, type SessionParseOptions, type SessionWriteEntry, type SessionWriteOp, type SessionWriteParseResult, type SourceHandle, type SourceKeyStore, type SourceMetadata, type SourceQueryOptions, SourceShreddedError, type StopFn, type StorageAdapter, type SyncResult, type TableDefinition, type TemplateRenderSpec, type TurnParams, type TurnResult, type UnresolvedLink, type UpsertByNaturalKeyOptions, type UserIdentity, type UserPreferences, type Viewer, type VisionOptions, type VisionSenderInput, WORKSPACES_SUBDIR, type WatchOptions, type WorkspacePaths, type WorkspaceRecord, type WorkspaceRegistry, type WriteHook, type WriteHookContext, type WritebackDefinition, type WritebackStateStore, type WritebackValidationResult, activeWorkspaceLabel, addWorkspace, adoptNativeEntities, analyticsEnabled, applyTokenBudget, applyWriteEntry, archiveLocalSqlite, assertSafeUrl, attachBlob, audiencePredicate, audienceViewSql, autoFtsColumns, autoUpdate, backfillOwnership, canManageRoles, classifyLinks, cloudRlsInstalled, configDir, contentHash, crawlUrl, createReadOnlyHeader, createS3Store, createSQLiteStateStore, decrypt, defaultWorkspaceYaml, deleteDbCredential, deleteToken, deriveCanonicalContexts, deriveKey, describeImage, describePdf, discoverCloudTables, enableAudienceView, enableChangelogRls, enableRlsForTable, encrypt, enrichKnowledge, ensureFtsIndex, ensureLatticeRoot, entityFileNames, estimateTokens, extractObjects, findLatticeRoot, fixSchemaConflicts, foldEntity, frontmatter, ftsTableName, fullTextSearch, generateEntryId, generateMemberPassword, generateWriteEntryId, getActiveWorkspace, getCloudSetting, getDbCredential, getOrCreateMasterKey, getWorkspace, grantCell, hasFtsIndex, hashFile, importLegacyUserConfig, installCloudRls, installCloudSettings, isEncrypted, isNativeEntity, isPostgresUrl, isPrivateIp, isRowAudience, isV1EntityFiles, listDbCredentials, listNativeBindings, listTokens, listWorkspaces, manifestPath, markdownTable, memberRoleName, migrateLatticeData, normalizeEntityFiles, observationVisible, observationsFromChange, openTargetLatticeForMigration, openUnderSource, organizeSource, parseConfigFile, parseConfigString, parseMarkdownEntries, parseMatches, parseObjects, parseSessionMD, parseSessionWrites, probeCloud, providerForUrl, provisionMemberRole, readIdentity, readManifest, readPreferences, readRegistry, readToken, referenceLocalFile, referenceUrl, registerNativeEntities, registryPath, resolveActiveS3Config, resolveLatticeRoot, resolveSource, resolveWorkspacePaths, revokeCell, revokeMemberRole, rootConfigDir, s3Key, saveDbCredential, saveDbCredentialForTeam, sealUnderSource, secureCloud, setActiveWorkspace, setCloudSetting, setRowVisibility, shredSource, slugify, summarizeText, tableNeedsAudienceView, toSafeDirName, truncate, validateEntryId, workspaceBlobsDir, workspaceConfigPath, workspaceContextDir, workspaceDataDir, workspaceDbPath, workspaceDir, workspacesDir, writeIdentity, writeManifest, writePreferences, writeRegistry, writeToken };
|