@lossless.org/objectstorage 11.0.0 → 11.2.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.
Files changed (51) hide show
  1. package/.smartconfig.json +19 -1
  2. package/dist_rust/ruststorage_linux_amd64 +0 -0
  3. package/dist_rust/ruststorage_linux_amd64.tsrust-build.json +4 -4
  4. package/dist_rust/ruststorage_linux_amd64_musl +0 -0
  5. package/dist_rust/ruststorage_linux_amd64_musl.tsrust-build.json +4 -4
  6. package/dist_rust/ruststorage_linux_arm64 +0 -0
  7. package/dist_rust/ruststorage_linux_arm64.tsrust-build.json +4 -4
  8. package/dist_rust/ruststorage_linux_arm64_musl +0 -0
  9. package/dist_rust/ruststorage_linux_arm64_musl.tsrust-build.json +4 -4
  10. package/dist_rust/ruststorage_macos_amd64 +0 -0
  11. package/dist_rust/ruststorage_macos_amd64.tsrust-build.json +4 -4
  12. package/dist_rust/ruststorage_macos_arm64 +0 -0
  13. package/dist_rust/ruststorage_macos_arm64.tsrust-build.json +4 -4
  14. package/dist_ts/00_commitinfo_data.js +1 -1
  15. package/dist_ts/classes.dashboard.d.ts +49 -0
  16. package/dist_ts/classes.dashboard.js +234 -0
  17. package/dist_ts/dashboard.bundled.d.ts +4 -0
  18. package/dist_ts/dashboard.bundled.js +20 -0
  19. package/dist_ts/dashboard.interfaces.d.ts +132 -0
  20. package/dist_ts/dashboard.interfaces.js +2 -0
  21. package/dist_ts/dashboard.plugins.d.ts +5 -0
  22. package/dist_ts/dashboard.plugins.js +10 -0
  23. package/dist_ts/index.d.ts +116 -1
  24. package/dist_ts/index.js +90 -2
  25. package/package.json +11 -7
  26. package/readme.md +400 -22
  27. package/third-party-notices.md +20 -4
  28. package/third_party_licenses/borsh-1.8.1-license.txt +23 -0
  29. package/third_party_licenses/borsh-derive-1.8.1-apache-2.0.txt +201 -0
  30. package/third_party_licenses/cargo-dependencies.html +420 -0
  31. package/third_party_licenses/dyn-clone-1.0.20-license.txt +23 -0
  32. package/third_party_licenses/equivalent-1.0.3-license.txt +25 -0
  33. package/third_party_licenses/hashbrown-0.17.1-license.txt +25 -0
  34. package/third_party_licenses/indexmap-2.14.2-license.txt +25 -0
  35. package/third_party_licenses/inventory.json +310 -8
  36. package/third_party_licenses/proc-macro-crate-3.5.0-license.txt +23 -0
  37. package/third_party_licenses/ref-cast-1.0.27-license.txt +23 -0
  38. package/third_party_licenses/ref-cast-impl-1.0.27-license.txt +23 -0
  39. package/third_party_licenses/schemars-1.2.3-license.txt +21 -0
  40. package/third_party_licenses/syn-3.0.9-license.txt +23 -0
  41. package/third_party_licenses/toml_datetime-1.1.2-license.txt +19 -0
  42. package/third_party_licenses/toml_edit-0.25.17-license.txt +19 -0
  43. package/third_party_licenses/toml_parser-1.1.5-license.txt +19 -0
  44. package/third_party_licenses/winnow-1.0.4-license.txt +18 -0
  45. package/ts/00_commitinfo_data.ts +1 -1
  46. package/ts/classes.dashboard.ts +280 -0
  47. package/ts/dashboard.bundled.ts.third-party-notices.json +1741 -0
  48. package/ts/dashboard.bundled.ts.third-party-notices.txt +5015 -0
  49. package/ts/dashboard.interfaces.ts +148 -0
  50. package/ts/dashboard.plugins.ts +13 -0
  51. package/ts/index.ts +217 -2
@@ -0,0 +1,132 @@
1
+ import type { ITypedRequest, implementsTR } from '@api.global/typedrequest-interfaces';
2
+ import type { ITypedSocketEvent } from '@api.global/typedsocket';
3
+ /**
4
+ * Engine snapshot shapes, as `ObjectStorage.getClusterSnapshot()` returns
5
+ * them. The engine builds them from memory only: the drive states the last
6
+ * heartbeat probe left, runtime stats, healing state, S3 listener counters
7
+ * and a bounded ring of recent events. Times are milliseconds since the Unix
8
+ * epoch; absent values are `null`.
9
+ */
10
+ export interface IObjectStorageTelemetryEvent {
11
+ /** Numbered per node from 1; a gap means older events left the ring. */
12
+ seq: number;
13
+ at: number;
14
+ kind: 'engine' | 'peer' | 'drive' | 'healing';
15
+ severity: 'info' | 'warning' | 'error';
16
+ message: string;
17
+ }
18
+ /** S3 listener counters since the engine started; bytes include HTTP headers. */
19
+ export interface IObjectStorageTrafficCounters {
20
+ requestsTotal: number;
21
+ errorResponses: number;
22
+ bytesIn: number;
23
+ bytesOut: number;
24
+ }
25
+ export interface IObjectStorageStorageTotals {
26
+ bucketCount: number;
27
+ objectCount: number;
28
+ totalBytes: number;
29
+ }
30
+ export interface IObjectStorageDriveSnapshot {
31
+ index: number;
32
+ path: string;
33
+ status: 'online' | 'degraded' | 'offline' | 'healing';
34
+ totalBytes: number;
35
+ usedBytes: number;
36
+ availableBytes: number;
37
+ errorCount: number;
38
+ lastError: string | null;
39
+ lastCheck: number | null;
40
+ erasureSetId: number | null;
41
+ }
42
+ export interface IObjectStorageHealingSnapshot {
43
+ active: boolean;
44
+ scanIntervalMs: number;
45
+ lastRunStartedAt: number | null;
46
+ lastRunCompletedAt: number | null;
47
+ lastDurationMs: number | null;
48
+ shardsChecked: number | null;
49
+ shardsHealed: number | null;
50
+ shardsPending: number | null;
51
+ failed: number | null;
52
+ lastError: string | null;
53
+ }
54
+ /** What one node reports about itself; local and remote nodes share this shape. */
55
+ export interface IObjectStorageNodeSnapshot {
56
+ /** Absent in standalone mode. */
57
+ nodeId: string | null;
58
+ mode: 'standalone' | 'cluster';
59
+ startedAt: number;
60
+ capturedAt: number;
61
+ traffic: IObjectStorageTrafficCounters;
62
+ storage: IObjectStorageStorageTotals;
63
+ /** Local drives (cluster mode); empty in standalone mode. */
64
+ drives: IObjectStorageDriveSnapshot[];
65
+ healing: IObjectStorageHealingSnapshot | null;
66
+ /** Recent events, oldest first. */
67
+ events: IObjectStorageTelemetryEvent[];
68
+ }
69
+ export interface IObjectStoragePeerSnapshot {
70
+ nodeId: string;
71
+ status: 'online' | 'suspect' | 'offline';
72
+ quicAddress: string;
73
+ s3Address: string;
74
+ driveCount: number;
75
+ lastHeartbeat: number;
76
+ missedHeartbeats: number;
77
+ /** The peer's own snapshot, when it answered within a second. */
78
+ snapshot: IObjectStorageNodeSnapshot | null;
79
+ unavailableReason: string | null;
80
+ }
81
+ export interface IObjectStorageErasureSnapshot {
82
+ dataShards: number;
83
+ parityShards: number;
84
+ chunkSizeBytes: number;
85
+ totalShards: number;
86
+ readQuorum: number;
87
+ writeQuorum: number;
88
+ erasureSetCount: number;
89
+ }
90
+ export interface IObjectStorageClusterViewSnapshot {
91
+ nodeId: string;
92
+ quorumHealthy: boolean;
93
+ majorityHealthy: boolean;
94
+ erasure: IObjectStorageErasureSnapshot;
95
+ peers: IObjectStoragePeerSnapshot[];
96
+ }
97
+ export interface IObjectStorageClusterSnapshot {
98
+ capturedAt: number;
99
+ clusterEnabled: boolean;
100
+ local: IObjectStorageNodeSnapshot;
101
+ /** Absent in standalone mode. */
102
+ cluster: IObjectStorageClusterViewSnapshot | null;
103
+ }
104
+ /**
105
+ * Dashboard configuration. The dashboard is off unless `enabled` is true.
106
+ */
107
+ export interface IObjectStorageDashboardConfig {
108
+ enabled: boolean;
109
+ /** Interface the dashboard listens on. Default `127.0.0.1`. */
110
+ address?: string;
111
+ /** TCP port; `0` asks the OS for one. Default 3001; must differ from the S3 port. */
112
+ port?: number;
113
+ }
114
+ /** One dashboard sample, as pushed to every connected dashboard. */
115
+ export interface IObjectStorageDashboardSample {
116
+ sampledAt: number;
117
+ /** The snapshot, or `null` when the engine could not answer. */
118
+ snapshot: IObjectStorageClusterSnapshot | null;
119
+ error: string | null;
120
+ /** Events were left out of peer snapshots to keep the sample within the push limit. */
121
+ eventsTruncated: boolean;
122
+ }
123
+ /** Answers with the latest sample, taking one when none exists yet. */
124
+ export interface IReq_ObjectStorageDashboardGetSample extends implementsTR<ITypedRequest, IReq_ObjectStorageDashboardGetSample> {
125
+ method: 'objectStorageDashboardGetSample';
126
+ request: Record<string, never>;
127
+ response: IObjectStorageDashboardSample;
128
+ }
129
+ /** Pushed every two seconds while at least one dashboard is connected. */
130
+ export interface IEvent_ObjectStorageDashboardSample extends ITypedSocketEvent<IObjectStorageDashboardSample> {
131
+ name: 'objectStorageDashboardSample';
132
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGFzaGJvYXJkLmludGVyZmFjZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9kYXNoYm9hcmQuaW50ZXJmYWNlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiIn0=
@@ -0,0 +1,5 @@
1
+ import * as typedrequest from '@api.global/typedrequest';
2
+ import * as typedsocket from '@api.global/typedsocket';
3
+ export { typedrequest, typedsocket };
4
+ import * as smartserve from '@push.rocks/smartserve';
5
+ export { smartserve };
@@ -0,0 +1,10 @@
1
+ // Loaded only by the dashboard module, which `ObjectStorage` imports
2
+ // dynamically when the dashboard is enabled.
3
+ // @api.global scope
4
+ import * as typedrequest from '@api.global/typedrequest';
5
+ import * as typedsocket from '@api.global/typedsocket';
6
+ export { typedrequest, typedsocket };
7
+ // @push.rocks scope
8
+ import * as smartserve from '@push.rocks/smartserve';
9
+ export { smartserve };
10
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGFzaGJvYXJkLnBsdWdpbnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9kYXNoYm9hcmQucGx1Z2lucy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxxRUFBcUU7QUFDckUsNkNBQTZDO0FBRTdDLG9CQUFvQjtBQUNwQixPQUFPLEtBQUssWUFBWSxNQUFNLDBCQUEwQixDQUFDO0FBQ3pELE9BQU8sS0FBSyxXQUFXLE1BQU0seUJBQXlCLENBQUM7QUFFdkQsT0FBTyxFQUFFLFlBQVksRUFBRSxXQUFXLEVBQUUsQ0FBQztBQUVyQyxvQkFBb0I7QUFDcEIsT0FBTyxLQUFLLFVBQVUsTUFBTSx3QkFBd0IsQ0FBQztBQUVyRCxPQUFPLEVBQUUsVUFBVSxFQUFFLENBQUMifQ==
@@ -1,4 +1,6 @@
1
1
  import * as plugins from './plugins.js';
2
+ import type { IObjectStorageClusterSnapshot, IObjectStorageDashboardConfig } from './dashboard.interfaces.js';
3
+ export * from './dashboard.interfaces.js';
2
4
  /**
3
5
  * Authentication configuration
4
6
  */
@@ -125,11 +127,33 @@ export interface IDriveConfig {
125
127
  */
126
128
  allowSharedDevice?: boolean;
127
129
  }
130
+ /**
131
+ * Mutual TLS for the cluster transport. Every file is read once at startup and
132
+ * must lie outside the control and drive roots, at an absolute path without
133
+ * symbolic links; a restart reloads them.
134
+ */
135
+ export interface IClusterTlsConfig {
136
+ /** PEM file with one or two CA certificates (two only during a CA rotation). */
137
+ caFile: string;
138
+ /**
139
+ * PEM file with this node's certificate, optionally followed by intermediates. It has one DNS SAN,
140
+ * equal to nodeId, and is valid for server and client authentication: an extended key usage
141
+ * extension listing serverAuth and clientAuth, or no extended key usage extension.
142
+ */
143
+ certFile: string;
144
+ /** PEM private key of certFile; owned by the engine's user, mode 0600 or 0400. */
145
+ keyFile: string;
146
+ }
128
147
  /**
129
148
  * Cluster configuration for distributed mode
130
149
  */
131
150
  export interface IClusterConfig {
132
151
  enabled: boolean;
152
+ /**
153
+ * This node's id. Generated and persisted when omitted, except with `tls`:
154
+ * then it is required and must be a lowercase DNS name equal to the node
155
+ * certificate's DNS subject alternative name.
156
+ */
133
157
  nodeId?: string;
134
158
  quicPort?: number;
135
159
  seedNodes?: string[];
@@ -147,11 +171,17 @@ export interface IClusterConfig {
147
171
  * 60,000 (1 minute) to 604,800,000 (7 days).
148
172
  */
149
173
  getDeadlineMs?: number;
174
+ /**
175
+ * Mutual TLS for the cluster transport. Without it the transport is
176
+ * encrypted but authenticates no peer. Every node of a cluster sets it, or
177
+ * none does: enabling it is a full stop of every node, not a rolling change.
178
+ */
179
+ tls?: IClusterTlsConfig;
150
180
  }
151
181
  /**
152
182
  * Configuration as the engine receives it: every section resolved.
153
183
  */
154
- export type TObjectStorageResolvedConfig = Required<Omit<IObjectStorageConfig, 'binaryPath'>>;
184
+ export type TObjectStorageResolvedConfig = Required<Omit<IObjectStorageConfig, 'binaryPath' | 'dashboard'>>;
155
185
  /**
156
186
  * Complete objectstorage configuration
157
187
  */
@@ -171,16 +201,32 @@ export interface IObjectStorageConfig {
171
201
  limits?: ILimitsConfig;
172
202
  multipart?: IMultipartConfig;
173
203
  cluster?: IClusterConfig;
204
+ /**
205
+ * The read-only visibility dashboard, served by this process on its own
206
+ * port. Off unless `enabled` is true; never sent to the engine.
207
+ */
208
+ dashboard?: IObjectStorageDashboardConfig;
174
209
  }
175
210
  /**
176
211
  * Logical bucket stats maintained by the Rust runtime.
177
212
  * Values are initialized from native storage on startup and updated on objectstorage mutations.
178
213
  */
214
+ /** A versioned bucket's S3 versioning status; a bucket that was never versioned has none. */
215
+ export type TBucketVersioningStatus = 'Enabled' | 'Suspended';
179
216
  export interface IBucketSummary {
180
217
  name: string;
218
+ /** Current objects; noncurrent versions and delete markers are counted below. */
181
219
  objectCount: number;
182
220
  totalSizeBytes: number;
183
221
  creationDate?: number;
222
+ /** Present only for a versioned bucket (standalone), and so are the counters below. */
223
+ versioning?: TBucketVersioningStatus;
224
+ /** Noncurrent data versions. */
225
+ noncurrentVersionCount?: number;
226
+ /** Payload bytes of the noncurrent data versions. */
227
+ noncurrentBytes?: number;
228
+ /** Delete markers, current and noncurrent. */
229
+ deleteMarkerCount?: number;
184
230
  }
185
231
  /**
186
232
  * Filesystem-level capacity snapshot for the storage directory or configured drive path.
@@ -278,6 +324,15 @@ export interface IStorageStats {
278
324
  buckets: IBucketSummary[];
279
325
  storageDirectory: string;
280
326
  storageLocations?: IStorageLocationSummary[];
327
+ /** Totals over the versioned buckets; absent without one. */
328
+ totalNoncurrentVersionCount?: number;
329
+ totalNoncurrentBytes?: number;
330
+ totalDeleteMarkerCount?: number;
331
+ /**
332
+ * How far version stamps run ahead of the wall clock, after the wall clock
333
+ * went backwards (or a forward jump was corrected); absent when they do not.
334
+ */
335
+ stampClockAheadOfWallMs?: number;
281
336
  }
282
337
  export interface IStorageStopResult {
283
338
  complete: true;
@@ -691,6 +746,8 @@ export interface IObjectStorageHealth {
691
746
  resourcePublicationHoldSupported: boolean;
692
747
  resourcePublicationHoldRequiresExternalDrain: false;
693
748
  resourceFencingReason?: string;
749
+ /** See `IStorageStats.stampClockAheadOfWallMs`. */
750
+ stampClockAheadOfWallMs?: number;
694
751
  }
695
752
  export declare const objectStorageResourceFenceErrorCodes: readonly ['EFENCE_REQUIRED', 'EFENCE_STALE', 'EFENCE_SCOPE_MISMATCH', 'EFENCE_UNSUPPORTED', 'EFENCE_MUTATION_MISMATCH', 'EFENCE_STATE_CORRUPT', 'EFENCE_RECEIPT_LIMIT', 'EFENCE_BUSY', 'EFENCE_OWNERSHIP_MISMATCH', 'EFENCE_PUBLICATION_HELD', 'EFENCE_PUBLICATION_MISMATCH', 'EFENCE_PUBLICATION_CAPABILITY'];
696
753
  export type TObjectStorageResourceFenceErrorCode = (typeof objectStorageResourceFenceErrorCodes)[number];
@@ -725,6 +782,33 @@ export interface IClusterPeerHealth {
725
782
  lastHeartbeat?: number;
726
783
  missedHeartbeats?: number;
727
784
  }
785
+ /**
786
+ * A drive's medium: `'rotational'` and `'flash'` (network volumes included),
787
+ * `'smr'` for drive-managed SMR, which reports itself as rotational and is
788
+ * never detected, and `'zoned'` for host-managed or host-aware SMR and ZNS.
789
+ */
790
+ export type TDriveMedia = 'rotational' | 'smr' | 'flash' | 'zoned';
791
+ /**
792
+ * A drive's media.
793
+ */
794
+ export interface IClusterDriveMedia {
795
+ /**
796
+ * The media the configuration declares for the drive. Not reported yet:
797
+ * drives cannot declare their media.
798
+ */
799
+ declared?: TDriveMedia;
800
+ /**
801
+ * What the engine detected once at startup, from the whole disk behind the
802
+ * drive root's filesystem (Linux sysfs `queue/zoned` and `queue/rotational`;
803
+ * a partition counts as its disk). `'unknown'` for filesystems without a
804
+ * block device of their own (tmpfs, overlay, network filesystems),
805
+ * device-mapper, md, loop and other virtual block devices, and on every
806
+ * platform but Linux. Virtual and network block devices report the
807
+ * rotational flag their driver or hypervisor sets (QEMU disks show
808
+ * `'rotational'` unless configured with `rotation_rate=1`).
809
+ */
810
+ detected: TDriveMedia | 'unknown';
811
+ }
728
812
  /**
729
813
  * Local drive health as measured by objectstorage's runtime probes.
730
814
  */
@@ -739,6 +823,7 @@ export interface IClusterDriveHealth {
739
823
  lastError?: string;
740
824
  lastCheck?: number;
741
825
  erasureSetId?: number;
826
+ media: IClusterDriveMedia;
742
827
  }
743
828
  export interface IClusterErasureHealth {
744
829
  dataShards: number;
@@ -762,6 +847,23 @@ export interface IClusterRepairHealth {
762
847
  failed?: number;
763
848
  lastError?: string;
764
849
  }
850
+ export interface IClusterTransportCertificate {
851
+ /** SHA-256 of the DER certificate, lowercase hex. */
852
+ sha256: string;
853
+ /** Milliseconds since the Unix epoch. */
854
+ notAfter: number;
855
+ }
856
+ /**
857
+ * How the cluster transport authenticates peers, and the certificates it
858
+ * loaded at startup.
859
+ */
860
+ export interface IClusterTransportHealth {
861
+ mode: 'mutualTls' | 'unauthenticated';
862
+ /** This node's certificate; only in mode 'mutualTls'. */
863
+ certificate?: IClusterTransportCertificate;
864
+ /** The loaded CA bundle in file order; only in mode 'mutualTls'. */
865
+ caCertificates?: IClusterTransportCertificate[];
866
+ }
765
867
  /**
766
868
  * Cluster runtime health from the Rust core.
767
869
  * When clustering is disabled, the response is `{ enabled: false }`.
@@ -777,6 +879,7 @@ export interface IClusterHealth {
777
879
  drives?: IClusterDriveHealth[];
778
880
  erasure?: IClusterErasureHealth;
779
881
  repairs?: IClusterRepairHealth;
882
+ transport?: IClusterTransportHealth;
780
883
  }
781
884
  /**
782
885
  * The result of one cluster healing scan, as `ObjectStorage.runHealingScan()`
@@ -829,11 +932,15 @@ export declare class ObjectStorage {
829
932
  private bridgeOwned;
830
933
  private stopRequested;
831
934
  private stopCompleted;
935
+ private readonly dashboardConfig;
936
+ private dashboard;
832
937
  constructor(configArg?: IObjectStorageConfig);
833
938
  start(): Promise<void>;
834
939
  private startInternal;
835
940
  /** Actual TCP port while running; the requested config port before/afterward. */
836
941
  get port(): number;
942
+ /** The dashboard's bound TCP port while it runs; `undefined` otherwise. */
943
+ get dashboardPort(): number | undefined;
837
944
  getStorageDescriptor(optionsArg?: Partial<plugins.tsclass.storage.IS3Descriptor>): Promise<plugins.tsclass.storage.IS3Descriptor>;
838
945
  private getEndpoint;
839
946
  private buildBucketTenantDescriptor;
@@ -902,6 +1009,14 @@ export declare class ObjectStorage {
902
1009
  */
903
1010
  replaceCredentials(credentials: IStorageCredential[]): Promise<void>;
904
1011
  getClusterHealth(): Promise<IClusterHealth>;
1012
+ /**
1013
+ * The dashboard's snapshot, built by the engine from memory only: it never
1014
+ * probes a drive, syncs or walks storage. A cluster node also asks each
1015
+ * online peer for its own snapshot, waiting up to a second per peer, in
1016
+ * parallel; a peer that does not answer is reported unavailable. One
1017
+ * snapshot runs at a time: a call while another runs rejects.
1018
+ */
1019
+ getClusterSnapshot(): Promise<IObjectStorageClusterSnapshot>;
905
1020
  /**
906
1021
  * Runs a healing scan on this cluster node and resolves with its result.
907
1022
  * The scan covers the manifests this node coordinated. The request joins