@lossless.org/objectstorage 11.0.0 → 11.1.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 +94 -1
  24. package/dist_ts/index.js +63 -1
  25. package/package.json +11 -7
  26. package/readme.md +192 -9
  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 +162 -1
@@ -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,6 +201,11 @@ 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.
@@ -725,6 +760,33 @@ export interface IClusterPeerHealth {
725
760
  lastHeartbeat?: number;
726
761
  missedHeartbeats?: number;
727
762
  }
763
+ /**
764
+ * A drive's medium: `'rotational'` and `'flash'` (network volumes included),
765
+ * `'smr'` for drive-managed SMR, which reports itself as rotational and is
766
+ * never detected, and `'zoned'` for host-managed or host-aware SMR and ZNS.
767
+ */
768
+ export type TDriveMedia = 'rotational' | 'smr' | 'flash' | 'zoned';
769
+ /**
770
+ * A drive's media.
771
+ */
772
+ export interface IClusterDriveMedia {
773
+ /**
774
+ * The media the configuration declares for the drive. Not reported yet:
775
+ * drives cannot declare their media.
776
+ */
777
+ declared?: TDriveMedia;
778
+ /**
779
+ * What the engine detected once at startup, from the whole disk behind the
780
+ * drive root's filesystem (Linux sysfs `queue/zoned` and `queue/rotational`;
781
+ * a partition counts as its disk). `'unknown'` for filesystems without a
782
+ * block device of their own (tmpfs, overlay, network filesystems),
783
+ * device-mapper, md, loop and other virtual block devices, and on every
784
+ * platform but Linux. Virtual and network block devices report the
785
+ * rotational flag their driver or hypervisor sets (QEMU disks show
786
+ * `'rotational'` unless configured with `rotation_rate=1`).
787
+ */
788
+ detected: TDriveMedia | 'unknown';
789
+ }
728
790
  /**
729
791
  * Local drive health as measured by objectstorage's runtime probes.
730
792
  */
@@ -739,6 +801,7 @@ export interface IClusterDriveHealth {
739
801
  lastError?: string;
740
802
  lastCheck?: number;
741
803
  erasureSetId?: number;
804
+ media: IClusterDriveMedia;
742
805
  }
743
806
  export interface IClusterErasureHealth {
744
807
  dataShards: number;
@@ -762,6 +825,23 @@ export interface IClusterRepairHealth {
762
825
  failed?: number;
763
826
  lastError?: string;
764
827
  }
828
+ export interface IClusterTransportCertificate {
829
+ /** SHA-256 of the DER certificate, lowercase hex. */
830
+ sha256: string;
831
+ /** Milliseconds since the Unix epoch. */
832
+ notAfter: number;
833
+ }
834
+ /**
835
+ * How the cluster transport authenticates peers, and the certificates it
836
+ * loaded at startup.
837
+ */
838
+ export interface IClusterTransportHealth {
839
+ mode: 'mutualTls' | 'unauthenticated';
840
+ /** This node's certificate; only in mode 'mutualTls'. */
841
+ certificate?: IClusterTransportCertificate;
842
+ /** The loaded CA bundle in file order; only in mode 'mutualTls'. */
843
+ caCertificates?: IClusterTransportCertificate[];
844
+ }
765
845
  /**
766
846
  * Cluster runtime health from the Rust core.
767
847
  * When clustering is disabled, the response is `{ enabled: false }`.
@@ -777,6 +857,7 @@ export interface IClusterHealth {
777
857
  drives?: IClusterDriveHealth[];
778
858
  erasure?: IClusterErasureHealth;
779
859
  repairs?: IClusterRepairHealth;
860
+ transport?: IClusterTransportHealth;
780
861
  }
781
862
  /**
782
863
  * The result of one cluster healing scan, as `ObjectStorage.runHealingScan()`
@@ -829,11 +910,15 @@ export declare class ObjectStorage {
829
910
  private bridgeOwned;
830
911
  private stopRequested;
831
912
  private stopCompleted;
913
+ private readonly dashboardConfig;
914
+ private dashboard;
832
915
  constructor(configArg?: IObjectStorageConfig);
833
916
  start(): Promise<void>;
834
917
  private startInternal;
835
918
  /** Actual TCP port while running; the requested config port before/afterward. */
836
919
  get port(): number;
920
+ /** The dashboard's bound TCP port while it runs; `undefined` otherwise. */
921
+ get dashboardPort(): number | undefined;
837
922
  getStorageDescriptor(optionsArg?: Partial<plugins.tsclass.storage.IS3Descriptor>): Promise<plugins.tsclass.storage.IS3Descriptor>;
838
923
  private getEndpoint;
839
924
  private buildBucketTenantDescriptor;
@@ -902,6 +987,14 @@ export declare class ObjectStorage {
902
987
  */
903
988
  replaceCredentials(credentials: IStorageCredential[]): Promise<void>;
904
989
  getClusterHealth(): Promise<IClusterHealth>;
990
+ /**
991
+ * The dashboard's snapshot, built by the engine from memory only: it never
992
+ * probes a drive, syncs or walks storage. A cluster node also asks each
993
+ * online peer for its own snapshot, waiting up to a second per peer, in
994
+ * parallel; a peer that does not answer is reported unavailable. One
995
+ * snapshot runs at a time: a call while another runs rejects.
996
+ */
997
+ getClusterSnapshot(): Promise<IObjectStorageClusterSnapshot>;
905
998
  /**
906
999
  * Runs a healing scan on this cluster node and resolves with its result.
907
1000
  * The scan covers the manifests this node coordinated. The request joins