sliftutils 1.7.9 → 1.7.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/bin/storageserver.js +4 -0
  2. package/{teststorage → examplestorage}/browser.tsx +18 -22
  3. package/{teststorage/server.ts → examplestorage/exampleserver.ts} +5 -5
  4. package/index.d.ts +528 -78
  5. package/misc/https/dns.d.ts +7 -3
  6. package/misc/https/dns.ts +4 -9
  7. package/misc/https/hostServer.d.ts +6 -3
  8. package/misc/https/hostServer.ts +6 -6
  9. package/package.json +2 -1
  10. package/spec.txt +77 -42
  11. package/storage/ArchivesDisk.d.ts +65 -0
  12. package/storage/ArchivesDisk.ts +365 -0
  13. package/storage/IArchives.d.ts +95 -1
  14. package/storage/IArchives.ts +146 -1
  15. package/storage/backblaze.d.ts +14 -2
  16. package/storage/backblaze.ts +18 -2
  17. package/storage/remoteStorage/ArchivesRemote.d.ts +29 -17
  18. package/storage/remoteStorage/ArchivesRemote.ts +63 -63
  19. package/storage/remoteStorage/ArchivesUrl.d.ts +46 -0
  20. package/storage/remoteStorage/ArchivesUrl.ts +74 -0
  21. package/storage/remoteStorage/accessPage.tsx +111 -28
  22. package/storage/remoteStorage/blobStore.d.ts +79 -25
  23. package/storage/remoteStorage/blobStore.ts +441 -287
  24. package/storage/remoteStorage/cliArgs.d.ts +1 -0
  25. package/storage/remoteStorage/cliArgs.ts +9 -0
  26. package/storage/remoteStorage/createArchives.d.ts +64 -0
  27. package/storage/remoteStorage/createArchives.ts +395 -0
  28. package/storage/remoteStorage/grantAccess.js +4 -0
  29. package/storage/remoteStorage/grantAccessCli.d.ts +1 -0
  30. package/storage/remoteStorage/grantAccessCli.ts +27 -0
  31. package/storage/remoteStorage/remoteConfig.d.ts +21 -0
  32. package/storage/remoteStorage/remoteConfig.ts +94 -0
  33. package/storage/remoteStorage/storageController.d.ts +19 -27
  34. package/storage/remoteStorage/storageController.ts +205 -136
  35. package/storage/remoteStorage/storageServer.d.ts +11 -0
  36. package/storage/remoteStorage/storageServer.ts +80 -93
  37. package/storage/remoteStorage/storageServerCli.d.ts +1 -0
  38. package/storage/remoteStorage/storageServerCli.ts +41 -0
  39. package/storage/remoteStorage/storageServerState.d.ts +37 -0
  40. package/storage/remoteStorage/storageServerState.ts +358 -0
  41. package/testsite/server.ts +1 -1
@@ -11,8 +11,12 @@ export declare function deleteRecord(type: string, key: string, value: string):
11
11
  export declare function setRecord(type: string, key: string, value: string, proxied?: "proxied"): Promise<void>;
12
12
  /** Keeps existing records */
13
13
  export declare function addRecord(type: string, key: string, value: string, proxied?: "proxied"): Promise<void>;
14
- /** Provide Cloudflare credentials directly (an API token, or a path to a file containing one), instead of relying on ./cloudflare.json */
14
+ /** Provide Cloudflare credentials directly instead of relying on ./cloudflare.json. Exactly one of
15
+ * key (the API token itself) or path (a file to read it from) — TypeScript rejects both/neither. */
15
16
  export declare function setCloudflareCredentials(config: {
16
- key?: string;
17
- path?: string;
17
+ value: {
18
+ key: string;
19
+ } | {
20
+ path: string;
21
+ };
18
22
  }): void;
package/misc/https/dns.ts CHANGED
@@ -115,15 +115,10 @@ export async function addRecord(type: string, key: string, value: string, proxie
115
115
 
116
116
 
117
117
  let credsOverride: { key: string } | undefined;
118
- /** Provide Cloudflare credentials directly (an API token, or a path to a file containing one), instead of relying on ./cloudflare.json */
119
- export function setCloudflareCredentials(config: { key?: string; path?: string }) {
120
- let key = config.key;
121
- if (!key && config.path) {
122
- key = fs.readFileSync(config.path, "utf8").trim();
123
- }
124
- if (!key) {
125
- throw new Error(`Must provide either key or path in setCloudflareCredentials, received ${JSON.stringify(Object.keys(config))}`);
126
- }
118
+ /** Provide Cloudflare credentials directly instead of relying on ./cloudflare.json. Exactly one of
119
+ * key (the API token itself) or path (a file to read it from) — TypeScript rejects both/neither. */
120
+ export function setCloudflareCredentials(config: { value: { key: string } | { path: string } }) {
121
+ let key = "key" in config.value ? config.value.key : fs.readFileSync(config.value.path, "utf8").trim();
127
122
  credsOverride = { key };
128
123
  }
129
124
 
@@ -2,9 +2,12 @@ export type HostServerConfig = {
2
2
  /** Full domain to host on (e.g. "testsite.example.com"). The HTTPS cert is created for this domain and *.domain, so using a subdomain never touches the root domain (beyond its _acme-challenge TXT record). */
3
3
  domain: string;
4
4
  port: number;
5
- /** Cloudflare API token (or a path to a file containing one). If neither is given, ./cloudflare.json is used. */
6
- cloudflareApiToken?: string;
7
- cloudflareApiTokenPath?: string;
5
+ /** Cloudflare API token: either the token string ({ key }) or a path to a file containing it ({ path }). Required pass { path: "./cloudflare.json" } explicitly for the on-disk file. */
6
+ cloudflareApiToken: {
7
+ key: string;
8
+ } | {
9
+ path: string;
10
+ };
8
11
  /** Creates an unproxied A record pointing domain at this machine (publicIp, or our detected external IP) */
9
12
  setDNSRecord?: boolean;
10
13
  publicIp?: string;
@@ -21,9 +21,10 @@ export type HostServerConfig = {
21
21
  /** Full domain to host on (e.g. "testsite.example.com"). The HTTPS cert is created for this domain and *.domain, so using a subdomain never touches the root domain (beyond its _acme-challenge TXT record). */
22
22
  domain: string;
23
23
  port: number;
24
- /** Cloudflare API token (or a path to a file containing one). If neither is given, ./cloudflare.json is used. */
25
- cloudflareApiToken?: string;
26
- cloudflareApiTokenPath?: string;
24
+
25
+ // TODO: Eventually we should support running without Cloudflare API tokens. It's annoying though as the user will have to create a self-signed certificate and then they'll have to go through and trust it everywhere, and a lot of the stuff is transparent, and so it'll have to be non-transparent, getting the user to go to the page that owns the domain and trust it from there. It's much better just make a Cloudflare account. You can buy a domain for $15 a year, and then you can use it for GitHub pages to host your own site and do all kinds of things just like any other real site.
26
+ /** Cloudflare API token: either the token string ({ key }) or a path to a file containing it ({ path }). Required — pass { path: "./cloudflare.json" } explicitly for the on-disk file. */
27
+ cloudflareApiToken: { key: string } | { path: string };
27
28
  /** Creates an unproxied A record pointing domain at this machine (publicIp, or our detected external IP) */
28
29
  setDNSRecord?: boolean;
29
30
  publicIp?: string;
@@ -33,9 +34,7 @@ export type HostServerConfig = {
33
34
  /** Hosts a SocketFunction server on a real domain, with an automatically created and renewed Let's Encrypt HTTPS certificate (cached in the home folder, shared between processes on the machine). Expose your controllers (and any RequireController setup) before calling this. Returns the mounted nodeId. */
34
35
  export async function hostServer(config: HostServerConfig): Promise<string> {
35
36
  let { domain, port } = config;
36
- if (config.cloudflareApiToken || config.cloudflareApiTokenPath) {
37
- setCloudflareCredentials({ key: config.cloudflareApiToken, path: config.cloudflareApiTokenPath });
38
- }
37
+ setCloudflareCredentials({ value: config.cloudflareApiToken });
39
38
  // The identity CA always lives on the root domain (nodeIds are threadHash.machineHash.root.tld)
40
39
  let rootDomain = domain.split(".").slice(-2).join(".");
41
40
  await loadIdentityCA(rootDomain);
@@ -56,6 +55,7 @@ export async function hostServer(config: HostServerConfig): Promise<string> {
56
55
 
57
56
  let nodeId = await SocketFunction.mount({
58
57
  public: true,
58
+ autoForwardPort: true,
59
59
  port,
60
60
  ...getThreadKeyCert(rootDomain),
61
61
  SNICerts: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sliftutils",
3
- "version": "1.7.9",
3
+ "version": "1.7.10",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -33,6 +33,7 @@
33
33
  "bin": {
34
34
  "filehoster": "./bin/filehoster.js",
35
35
  "autohost": "./bin/autohost.js",
36
+ "storageserver": "./bin/storageserver.js",
36
37
  "build-nodejs": "./builders/nodeJSBuildRun.js",
37
38
  "buildnodejs": "./builders/nodeJSBuildRun.js",
38
39
  "build-extension": "./builders/extensionBuildRun.js",
package/spec.txt CHANGED
@@ -1,60 +1,95 @@
1
1
 
2
- Searching
3
- 1) We want to create something that's database agnostic
4
- - So I think we need like multiple phases for reading. And for applying it? We need to be able to know all of our reads ahead of time, which we should be able to roughly know. Or at least we'll have multiple stages. That should be fine. I guess we could do it so it actually understands our database. Like it reads, and then if it's not there, it returns. That might be fine too.
5
2
 
6
- interface Database<Root> {
7
- readData: <T>(fnc: (root: Root) => T) => T | undefined;
8
- writeData: <T>(fnc: (root: Root) => T, newValue: T) => void;
9
- }
10
- I guess this works. We need multiple stages, so it's gonna have to read in the index, In parallel, and then once it has the full index, then it can do the next step in parallel, etc.
3
+ IMPORTANT! modified time PLUS size is what's important. Store both!
11
4
 
12
5
 
13
- remaining
14
- 1) The code book creation. We kind of do want to recreate it sometimes. Maybe we can have something that does a full expensive recreation at certain early on points?
15
- 2) The searching there's a few different stages. Like, we want to search more IVF cells, but then even within the cells, we kind of want to download more of the final embeddings. because the codes aren't gonna to be perfect. So we have to do both at the same time?
6
+ 1) Tests locally, running it in Slift utils and running some code in CYOA to access it.
7
+ 2) Set up a bootstrapper that we can chain with another command that will set up secrets, cloudflare key, etc
8
+ yarn exposesecrets && yarn storageserver --url https://storage.vidgridweb.com:4446 --folder ~/storagetest
9
+ - The bootstrapper needs to write to "cloudflare.json" and "backblaze.json"
10
+ - Using our sync server secrets. Not sure if they're stored in there, but, you know, easy enough...
16
11
 
17
- parts
18
- p8g8 format (q8), so we can store 2048 dimensions is a bit over 2KB
19
- IVF cell size 32? PQ index reduces by 32X, and if IVF cells reduce by 32X... then 500K 2KB values (1GB) would still just be... 1MB IVF index, so... that's good.
20
- 0) In IVF, sample 32, then keep sampling more, up to 128 cells
21
- 1) Pq 64 index (byte values each, break into 64 groups, always... should work well, For face vectors, they're already really optimized. They have so few dimensions, and so we don't compress it by that much. But for text vectors, they're less compressed, so we compress it more. So it should work in all cases)
22
- 2) Retraining index on a subset
23
- - Or... just don't. Freeze after a certain point, and... it might be fine? HMM... Although no, we could allow retraining. It should be cheap enough.
24
- 3) Split into transaction logs, so we can read the PQ index efficiently, but still update it
25
- UGH... I mean basically you would just be replicating some of what we actually store on disk, but in our database, which is stupid, but I think we'll probably do it anyway. Basically, just a transaction log. We have to read everything every time, but it should help the speed a little bit...
26
- - And I think it's fair because it's us saying, hey, we're going to read all this data every time, which the database can't know, so it can't optimize for that, but we can know, and then we can optimize for it
27
- 4) IVF the pq index to vastly decrease the initial access time
28
- 5.0) automatically split cells when they get big enough (2X their initial size)
29
- 5) occasionally regenerate the entire IVF.
30
- 5.1) Have something to store a subset of the data codes?
31
- 5.1) Have something that stores some of the data randomly so we can easily access a subset. We won't represent deletions in it. That's fine. This will allow us to rebalance the IVF fairly efficiently.
32
- - And maybe even slowly move stuff over to the new IVF as we access stuff, we see if it's in the new IVF, and if not, we move it?
33
- - 10% of the data? And... maybe just the pq index values?
34
- - Should make retraining the IVF ten times faster and shouldn't affect the recall by too much, especially if we have more cells
35
- - AND, Maybe we can actually sample less the more data we have. Ramping up to 10%.
36
- unknown
37
- exactly how we're going to update the IVF.
38
- IVF pq index, so we can read part of it
39
- mini transaction logs in each cell?
12
+ 3) Verify backblaze works, verify basically all the features work.
40
13
 
41
- BUT... how do we update IVF
42
14
 
43
15
 
44
16
 
17
+ 3) Support sharding in synchronization sources as well, adding new field
18
+ route: {
19
+ range: [number, number];
20
+ level: number;
21
+ }
22
+ - Route level is required as if two things have different route ranges, they obviously aren't going to be pulling from each other. So we need another way to stack redundancy, and the route level can do that. If two things are at the same route level, it means they aren't using each other for redundancy. We could automatically detect this, but it's more clear if it's explicit.
45
23
 
46
- 1) Converting once is faster, so we should investigate if we can have a pool of memory. If we can reuse it, then the allocations are free.
24
+ 3.1) We will almost always set up copy files on our different shards, but of course, these will only be considered depending on the valid window.
25
+ 3.2) Of course, having our site configuration support sharding as well. So you don't need to fiddle with the shards. You kind of just say there are a certain number of shards. And if you want to change the shards, it will automatically set them up in the future, applying a default switch time of a day that you can also configure yourself, etc.
47
26
 
48
- 1) Fix embedding formats. It's really, really stupid. decodeToLength was just absolutely retarded.
49
27
 
50
28
 
51
29
 
30
+ 5) Shard retry writes - Basically, they give us part of a path, and part of it we can fill in ourselves. We try to keep track of which shards are down and if a shard is down, we will keep changing that random value until we find something that doesn't match that shard Until we hit a certain percentage of shards being down, probably greater than 99%. And then we return the path that we ended up using.
31
+ - This solves the problem of not being able to write to the backup sources. As it'll mean you'll be able to read if a shard is down, and you'll also be able to write if a shard is down. So it'll be like everything's fine.
32
+ - We only need to use this for certain important things like uploading images. pretty much all of the AI stuff, we can probably use it.
33
+ HMMM!!! This causes problems if we want it to be an image or a video. So we should probably have it so by default we try to put nothing there, So it'll be the exact path the user requested. That way, if they try to do the same right multiple times, it'll clobber itself.
34
+ - Although realistically, I don't think it'll really matter. because we're not generating the same content most of the time.
35
+ 6) geographical sharding as well. Which tries to change the hash so it's the server that we're closest to
36
+ - This can massively reduce the write latency, Which is important because we don't want to have to send the data around all the time, we just want to send around the URLs. Well then this requires waiting for the remote to confirm that we can read back. which will be purely limited really on latency if it's close to us.
52
37
 
38
+ 7) readerDiskLimit. Only when our first sync source has copyFiles is false and cacheReads is true (and required true). Once we copy over a certain amount, we start deleting the least recently used from our local cache. This allows us to cache large sources without getting overwhelmed.
53
39
 
54
40
 
55
- 0) Make sure the AI stops trying to stop when a read didn't work because it doesn't fucking matter. I don't think there's a single place that's doing it where it actually matters.
56
- - I mean, I guess there are a few places. Like when we're rebalancing, that's one place.
57
- - I guess some of the returns are valid, but I guess we have to check all of them.
58
41
 
59
- 1) Make sure the AI gets rid of all those four each's. I mean, what the fuck?
60
- 1) Ensure the AI efficiently switches from the hard-coded stuff to the regular stuff and correctly regenerates the codebook.
42
+
43
+
44
+
45
+
46
+
47
+
48
+
49
+ 2) Use our machine configuration to setup a storage service
50
+
51
+ 2.1) cyoa page to setup services + VIEW their storagerouting.JSON
52
+ WE DO NOT want to configure it via the interface! We want the configuration to always be in code. That way, we can update one place and all of our buckets will update!
53
+ OH! Make something that will import all of our code just like in a deploy, and then we'll gather all of the bucket configurations that we created using our helper functions so we register them all, and then we'll touch them all to make sure the configuration is updated on all of them!
54
+ - We should probably make a factory so we can easily create buckets without having to find the full chain every single time, and so we can change the full chain easily in one place.
55
+ - We should allow easily chaining them for redundancy
56
+ - We might even have some UI that shows all of the storage services together, allowing you to easily configure how they chain, etc.
57
+ - We should also have it poll for the status of like scanning file stats, etc.
58
+
59
+ 2.2) Test some of these custom buckets on CYOA (local dev), testing redundancy, speed, etc.
60
+ - If we run into a lot of issues that we need to debug, we can always host the storage server locally.
61
+
62
+ custom => backblaze
63
+ - So internal redundancy and then also backblaze at the very end
64
+ 2.3) Test using our thing as a front end for back plays with no copy, which should mean there's really no setup.
65
+ - Just test reading images or something.
66
+
67
+
68
+
69
+
70
+
71
+
72
+
73
+ ArchivesRemote
74
+ bucketName
75
+ public
76
+ fast
77
+ writeDelay
78
+
79
+ ArchivesFactory
80
+ host: https://storage.vidgridweb.com:4444/storagerouting.json
81
+ redundantHosts:
82
+ - https://storage2.vidgridweb.com:4445/storagerouting.json
83
+ - https://f002.backblazeb2.com/file/querysubtest-com-public-immutable/storage/storagerouting.json
84
+
85
+ HostStorageServerConfig
86
+ host: https://storage.vidgridweb.com:4444/storagerouting.json
87
+ redundantHosts:
88
+ - https://storage.vidgridweb.com:4444/storagerouting.json
89
+ - https://storage2.vidgridweb.com:4445/storagerouting.json
90
+ - https://f002.backblazeb2.com/file/querysubtest-com-public-immutable/storage/storagerouting.json
91
+ folder: os.homedir() + "/storage"
92
+ cloudflareApiToken: ...
93
+
94
+
95
+ https://f002.backblazeb2.com/file/querysubtest-com-public-immutable/image/img_00BloAFyIevA_1024x1536.jpg
@@ -0,0 +1,65 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ import { IArchives, ArchiveFileInfo, ArchivesConfig } from "./IArchives";
4
+ export declare class ArchivesDisk implements IArchives {
5
+ private folder;
6
+ constructor(folder: string);
7
+ private filesDir;
8
+ private uploadsDir;
9
+ private handles;
10
+ private largeUploads;
11
+ private nextLargeUploadId;
12
+ init: {
13
+ (): Promise<void>;
14
+ reset(): void;
15
+ set(newValue: Promise<void>): void;
16
+ };
17
+ getDebugName(): string;
18
+ getConfig(): Promise<ArchivesConfig>;
19
+ private filePath;
20
+ set(key: string, data: Buffer, config?: {
21
+ lastModified?: number;
22
+ }): Promise<void>;
23
+ del(key: string): Promise<void>;
24
+ get(key: string, config?: {
25
+ range?: {
26
+ start: number;
27
+ end: number;
28
+ };
29
+ }): Promise<Buffer | undefined>;
30
+ get2(key: string, config?: {
31
+ range?: {
32
+ start: number;
33
+ end: number;
34
+ };
35
+ }): Promise<{
36
+ data: Buffer;
37
+ writeTime: number;
38
+ } | undefined>;
39
+ getInfo(key: string): Promise<{
40
+ writeTime: number;
41
+ size: number;
42
+ } | undefined>;
43
+ find(prefix: string, config?: {
44
+ shallow?: boolean;
45
+ type: "files" | "folders";
46
+ }): Promise<string[]>;
47
+ findInfo(prefix: string, config?: {
48
+ shallow?: boolean;
49
+ type?: "files" | "folders";
50
+ }): Promise<ArchiveFileInfo[]>;
51
+ private collectFiles;
52
+ setLargeFile(config: {
53
+ path: string;
54
+ getNextData(): Promise<Buffer | undefined>;
55
+ }): Promise<void>;
56
+ startLargeUpload(): Promise<string>;
57
+ appendLargeUpload(id: string, data: Buffer): Promise<void>;
58
+ finishLargeUpload(id: string, key: string): Promise<void>;
59
+ cancelLargeUpload(id: string): Promise<void>;
60
+ getURL(path: string): Promise<string>;
61
+ }
62
+ export declare function applyFindInfoShape(files: ArchiveFileInfo[], prefix: string, config?: {
63
+ shallow?: boolean;
64
+ type?: "files" | "folders";
65
+ }): ArchiveFileInfo[];