@krovacloud/sdk 0.3.9 → 0.3.11

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 CHANGED
@@ -49,6 +49,14 @@ const cube = await krova.cubes.create("space_123", {
49
49
  console.log(`Created cube ${cube.id} (${cube.state})`);
50
50
  console.log(` ${cube.resources.vcpu} vCPU / ${cube.resources.ramGb} GB RAM, image ${cube.image}`);
51
51
 
52
+ // Everything needed to SSH in — including WHICH USER to log in as.
53
+ // Read `user`; do not derive it from `image`. It is "ubuntu" or "debian" on
54
+ // Cubes from images that ship a default user (both with passwordless sudo)
55
+ // and "root" on Cubes created before that change, so the image id cannot
56
+ // tell you. `cube.sshUser` on the Cube object carries the same value.
57
+ const ssh = await krova.cubes.ssh("space_123", cube.id);
58
+ console.log(`ssh ${ssh.user}@${ssh.host} -p ${ssh.port}`);
59
+
52
60
  // Power it off, then start it again
53
61
  await krova.cubes.powerOff("space_123", cube.id);
54
62
  await krova.cubes.wake("space_123", cube.id);
@@ -120,7 +128,7 @@ const cube = await krova.cubes.create(
120
128
  image: "ubuntu-24.04",
121
129
  resources: { vcpu: 2, ramGb: 4, diskGb: 40 },
122
130
  sshPublicKey: "ssh-ed25519 AAAA... you@host",
123
- region: "us-east", // optional — slug from catalog.regions()
131
+ region: "us", // optional — slug from catalog.regions()
124
132
  userData: "#cloud-config\n", // optional — cloud-init (max 16 KB)
125
133
  },
126
134
  { idempotencyKey: "deploy-2026-07-01" },
@@ -301,6 +309,34 @@ The package ships its own type declarations — no `@types/*` install needed. `C
301
309
 
302
310
  See [CONTRIBUTING.md](https://github.com/krovacloud/krova-node/blob/main/CONTRIBUTING.md). Report security issues privately per [SECURITY.md](https://github.com/krovacloud/krova-node/blob/main/SECURITY.md).
303
311
 
312
+
313
+ ## Custom domains: HTTPS to the Cube
314
+
315
+ By default the edge reaches your Cube over cleartext HTTP — visitors are on
316
+ HTTPS either way, since TLS terminates at the edge. Set `originScheme: "https"`
317
+ when the Cube terminates TLS *itself*: a control panel holding its own
318
+ certificate, or an app listening on HTTPS, answers plain HTTP with a redirect
319
+ and cannot be served over cleartext at all.
320
+
321
+ ```ts
322
+ // When attaching the domain…
323
+ await krova.domains.create(spaceId, cubeId, {
324
+ domain: "panel.example.com",
325
+ port: 443,
326
+ originScheme: "https",
327
+ });
328
+
329
+ // …or switch an already-attached domain over.
330
+ await krova.domains.update(spaceId, cubeId, mappingId, {
331
+ originScheme: "https",
332
+ });
333
+ ```
334
+
335
+ The dial port is derived: `https` on the default port 80 connects on 443, and a
336
+ deliberate custom port is honoured exactly. The setting is verified against the
337
+ Cube before it is applied — if the domain does not serve, the route is left on
338
+ `http`.
339
+
304
340
  ## License
305
341
 
306
342
  [MIT](./LICENSE) © 2026 Krova Inc.
package/dist/index.d.cts CHANGED
@@ -366,7 +366,7 @@ interface paths {
366
366
  ramGb: number;
367
367
  diskGb: number;
368
368
  };
369
- /** @description SSH public key written to /root/.ssh/authorized_keys at boot. Must start with ssh-ed25519, ssh-rsa, ecdsa-sha2-*, ssh-dss, or sk-*@openssh.com. */
369
+ /** @description SSH public key written to the Cube login user's authorized_keys at boot (~/.ssh/authorized_keys for the ubuntu or debian user; /root/.ssh/authorized_keys on Cubes created before the default-user change). GET /cubes/{cubeId}/ssh reports the login user for a given Cube. Must start with ssh-ed25519, ssh-rsa, ecdsa-sha2-*, ssh-dss, or sk-*@openssh.com. */
370
370
  sshPublicKey: string;
371
371
  /** @description Region slug from /v1/regions (optional). */
372
372
  region?: string;
@@ -557,7 +557,7 @@ interface paths {
557
557
  put?: never;
558
558
  /**
559
559
  * Restart a running Cube
560
- * @description Cold-restarts the Cube: the hypervisor process is stopped and relaunched, so the Cube boots against the host's current kernel. This is how a Cube picks up a refreshed guest kernel after a platform image update — a `reboot` issued inside the Cube cannot do it, because the kernel is supplied externally by the host. Disk state is preserved; only the kernel changes. The Cube must be `running` (a stopped Cube already picks up the latest kernel when started). Concurrent restarts of the same Cube are rejected with 409 rather than queued twice.
560
+ * @description Cold-restarts the Cube: the hypervisor process is stopped and relaunched, so the Cube boots against the host's current kernel. This is how a Cube picks up a refreshed guest kernel after a platform image update — a `reboot` issued inside the Cube cannot do it, because the kernel is supplied externally by the host. Disk state is preserved; only the kernel changes. Cube must be `running` (a stopped Cube already picks up the latest kernel when started). Concurrent restarts of the same Cube are rejected with 409 rather than queued twice.
561
561
  */
562
562
  post: {
563
563
  parameters: {
@@ -654,7 +654,7 @@ interface paths {
654
654
  };
655
655
  /**
656
656
  * Get a Cube's SSH connection info
657
- * @description Host (server public IPv4), host SSH port, login user, and any captured SSH host public keys. `hostKeys` is empty until Krova captures cube host keys clients fall back to trust-on-first-use until then.
657
+ * @description Host (server public IPv4), host SSH port, login user, and the Cube's captured SSH host public keys. **Use `user` rather than assuming a username** — it is `ubuntu` or `debian` on Cubes created from images that ship a default user, and `root` on Cubes created before that change or imported from a rootfs without one. `hostKeys` is empty only until the reachability cron makes its first capture; clients fall back to trust-on-first-use until then, and pin strictly afterwards.
658
658
  */
659
659
  get: {
660
660
  parameters: {
@@ -744,7 +744,7 @@ interface paths {
744
744
  "application/json": {
745
745
  /** @description The hostname to attach, e.g. app.example.com */
746
746
  domain: string;
747
- /** @description The Cube port the domain proxies to. */
747
+ /** @description Cube port the domain proxies to. */
748
748
  port: number;
749
749
  /**
750
750
  * @description enforced = proxy sets platform security headers (HSTS, X-Frame-Options, …); app_managed = the app's own headers pass through. Default enforced.
@@ -769,6 +769,26 @@ interface paths {
769
769
  } | null;
770
770
  /** @description Max request body size (MB). Null = unlimited (default). */
771
771
  maxRequestBodyMb?: number | null;
772
+ /** @description Per-domain CORS policy, applied at the edge. Null (default) = CORS disabled and no Access-Control-* header is emitted anywhere. When set, the proxy answers OPTIONS preflight itself (204, the cube is never involved) and adds the headers to normal AND error responses, so a cross-origin request to a failing or sleeping cube is never CORS-masked. */
773
+ corsConfig?: {
774
+ /** @description Exact scheme://host[:port] origins (no path, query or trailing slash), or the single-element wildcard ["*"]. "*" and allowCredentials are mutually exclusive. */
775
+ allowedOrigins: string[];
776
+ /** @description Allowed methods. Omitted = GET, POST, PUT, PATCH, DELETE, OPTIONS. */
777
+ allowedMethods?: string[];
778
+ /** @description Allowed request headers. Omitted/empty = echo the preflight's Access-Control-Request-Headers. */
779
+ allowedHeaders?: string[];
780
+ /** @description Response headers exposed to JS. Omitted/empty = none. */
781
+ exposedHeaders?: string[];
782
+ /** @description Send Access-Control-Allow-Credentials: true. Rejected together with the "*" origin. */
783
+ allowCredentials?: boolean;
784
+ /** @description Preflight cache TTL. Default 600; 86400 is Chromium's hard cap. */
785
+ maxAgeSeconds?: number;
786
+ } | null;
787
+ /**
788
+ * @description Scheme the edge speaks to the CUBE on the backend hop. http (default) = cleartext. https = the cube terminates TLS itself (a control panel holding its own certificate, or an app listening on HTTPS) and answers plain HTTP with a redirect, so it cannot be reached over cleartext at all. Visitors are on HTTPS either way. The dial port is derived: https on the default port 80 connects on 443; a deliberate custom port is honoured exactly. Verified against the cube before it is applied — if the domain does not serve, the route is left on http.
789
+ * @enum {string}
790
+ */
791
+ originScheme?: "http" | "https";
772
792
  /** @description Edge gzip/zstd compression. Default false (domains behind a CDN are already compressed there). */
773
793
  responseCompression?: boolean;
774
794
  /** @description Visitor IP/CIDR allow-list (v4+v6). Non-empty ⇒ only these reach the app. Null/empty = open. */
@@ -885,6 +905,26 @@ interface paths {
885
905
  } | null;
886
906
  /** @description Max request body size (MB). Null = unlimited (default). */
887
907
  maxRequestBodyMb?: number | null;
908
+ /** @description Per-domain CORS policy, applied at the edge. Null (default) = CORS disabled and no Access-Control-* header is emitted anywhere. When set, the proxy answers OPTIONS preflight itself (204, the cube is never involved) and adds the headers to normal AND error responses, so a cross-origin request to a failing or sleeping cube is never CORS-masked. */
909
+ corsConfig?: {
910
+ /** @description Exact scheme://host[:port] origins (no path, query or trailing slash), or the single-element wildcard ["*"]. "*" and allowCredentials are mutually exclusive. */
911
+ allowedOrigins: string[];
912
+ /** @description Allowed methods. Omitted = GET, POST, PUT, PATCH, DELETE, OPTIONS. */
913
+ allowedMethods?: string[];
914
+ /** @description Allowed request headers. Omitted/empty = echo the preflight's Access-Control-Request-Headers. */
915
+ allowedHeaders?: string[];
916
+ /** @description Response headers exposed to JS. Omitted/empty = none. */
917
+ exposedHeaders?: string[];
918
+ /** @description Send Access-Control-Allow-Credentials: true. Rejected together with the "*" origin. */
919
+ allowCredentials?: boolean;
920
+ /** @description Preflight cache TTL. Default 600; 86400 is Chromium's hard cap. */
921
+ maxAgeSeconds?: number;
922
+ } | null;
923
+ /**
924
+ * @description Scheme the edge speaks to the CUBE on the backend hop. http (default) = cleartext. https = the cube terminates TLS itself (a control panel holding its own certificate, or an app listening on HTTPS) and answers plain HTTP with a redirect, so it cannot be reached over cleartext at all. Visitors are on HTTPS either way. The dial port is derived: https on the default port 80 connects on 443; a deliberate custom port is honoured exactly. Verified against the cube before it is applied — if the domain does not serve, the route is left on http.
925
+ * @enum {string}
926
+ */
927
+ originScheme?: "http" | "https";
888
928
  /** @description Edge gzip/zstd compression. Default false (domains behind a CDN are already compressed there). */
889
929
  responseCompression?: boolean;
890
930
  /** @description Visitor IP/CIDR allow-list (v4+v6). Non-empty ⇒ only these reach the app. Null/empty = open. */
@@ -1283,10 +1323,12 @@ interface paths {
1283
1323
  name: string;
1284
1324
  fileSizeBytes: number;
1285
1325
  /**
1326
+ * @description What to do with SSH access on the imported rootfs. `replace` (default) writes `sshPublicKey` to the login user's authorized_keys and requires `sshPublicKey` to be set. `keep` leaves the image's own authorized_keys and sshd configuration completely untouched — nothing is written — so you must already hold a private key that the image accepts, or you will not be able to log in.
1286
1327
  * @default replace
1287
1328
  * @enum {string}
1288
1329
  */
1289
1330
  sshKeyMode?: "replace" | "keep";
1331
+ /** @description Public key to install when `sshKeyMode` is `replace`. Written to the login user's `~/.ssh/authorized_keys` — which user that is comes from the imported rootfs itself, so an image without a Krova default user keeps logging in as root. Ignored when `sshKeyMode` is `keep`. */
1290
1332
  sshPublicKey?: string | null;
1291
1333
  region?: string | null;
1292
1334
  vcpusOverride?: number | null;
@@ -1726,6 +1768,7 @@ interface components {
1726
1768
  diskGb: number;
1727
1769
  };
1728
1770
  image: string;
1771
+ sshUser: string;
1729
1772
  costPerHour: number;
1730
1773
  /** Format: date-time */
1731
1774
  createdAt: string;
@@ -1748,6 +1791,7 @@ interface components {
1748
1791
  name: string;
1749
1792
  version: string;
1750
1793
  description: string;
1794
+ defaultUser: string;
1751
1795
  };
1752
1796
  PricingTier: {
1753
1797
  minVcpus: number;
@@ -1998,6 +2042,7 @@ declare class KrovaClient {
1998
2042
  diskGb: number;
1999
2043
  };
2000
2044
  image: string;
2045
+ sshUser: string;
2001
2046
  costPerHour: number;
2002
2047
  createdAt: string;
2003
2048
  updatedAt: string;
@@ -2189,6 +2234,7 @@ declare class KrovaClient {
2189
2234
  name: string;
2190
2235
  version: string;
2191
2236
  description: string;
2237
+ defaultUser: string;
2192
2238
  }[];
2193
2239
  }>;
2194
2240
  /** Per-resource hourly rates and volume pricing tiers. */
package/dist/index.d.ts CHANGED
@@ -366,7 +366,7 @@ interface paths {
366
366
  ramGb: number;
367
367
  diskGb: number;
368
368
  };
369
- /** @description SSH public key written to /root/.ssh/authorized_keys at boot. Must start with ssh-ed25519, ssh-rsa, ecdsa-sha2-*, ssh-dss, or sk-*@openssh.com. */
369
+ /** @description SSH public key written to the Cube login user's authorized_keys at boot (~/.ssh/authorized_keys for the ubuntu or debian user; /root/.ssh/authorized_keys on Cubes created before the default-user change). GET /cubes/{cubeId}/ssh reports the login user for a given Cube. Must start with ssh-ed25519, ssh-rsa, ecdsa-sha2-*, ssh-dss, or sk-*@openssh.com. */
370
370
  sshPublicKey: string;
371
371
  /** @description Region slug from /v1/regions (optional). */
372
372
  region?: string;
@@ -557,7 +557,7 @@ interface paths {
557
557
  put?: never;
558
558
  /**
559
559
  * Restart a running Cube
560
- * @description Cold-restarts the Cube: the hypervisor process is stopped and relaunched, so the Cube boots against the host's current kernel. This is how a Cube picks up a refreshed guest kernel after a platform image update — a `reboot` issued inside the Cube cannot do it, because the kernel is supplied externally by the host. Disk state is preserved; only the kernel changes. The Cube must be `running` (a stopped Cube already picks up the latest kernel when started). Concurrent restarts of the same Cube are rejected with 409 rather than queued twice.
560
+ * @description Cold-restarts the Cube: the hypervisor process is stopped and relaunched, so the Cube boots against the host's current kernel. This is how a Cube picks up a refreshed guest kernel after a platform image update — a `reboot` issued inside the Cube cannot do it, because the kernel is supplied externally by the host. Disk state is preserved; only the kernel changes. Cube must be `running` (a stopped Cube already picks up the latest kernel when started). Concurrent restarts of the same Cube are rejected with 409 rather than queued twice.
561
561
  */
562
562
  post: {
563
563
  parameters: {
@@ -654,7 +654,7 @@ interface paths {
654
654
  };
655
655
  /**
656
656
  * Get a Cube's SSH connection info
657
- * @description Host (server public IPv4), host SSH port, login user, and any captured SSH host public keys. `hostKeys` is empty until Krova captures cube host keys clients fall back to trust-on-first-use until then.
657
+ * @description Host (server public IPv4), host SSH port, login user, and the Cube's captured SSH host public keys. **Use `user` rather than assuming a username** — it is `ubuntu` or `debian` on Cubes created from images that ship a default user, and `root` on Cubes created before that change or imported from a rootfs without one. `hostKeys` is empty only until the reachability cron makes its first capture; clients fall back to trust-on-first-use until then, and pin strictly afterwards.
658
658
  */
659
659
  get: {
660
660
  parameters: {
@@ -744,7 +744,7 @@ interface paths {
744
744
  "application/json": {
745
745
  /** @description The hostname to attach, e.g. app.example.com */
746
746
  domain: string;
747
- /** @description The Cube port the domain proxies to. */
747
+ /** @description Cube port the domain proxies to. */
748
748
  port: number;
749
749
  /**
750
750
  * @description enforced = proxy sets platform security headers (HSTS, X-Frame-Options, …); app_managed = the app's own headers pass through. Default enforced.
@@ -769,6 +769,26 @@ interface paths {
769
769
  } | null;
770
770
  /** @description Max request body size (MB). Null = unlimited (default). */
771
771
  maxRequestBodyMb?: number | null;
772
+ /** @description Per-domain CORS policy, applied at the edge. Null (default) = CORS disabled and no Access-Control-* header is emitted anywhere. When set, the proxy answers OPTIONS preflight itself (204, the cube is never involved) and adds the headers to normal AND error responses, so a cross-origin request to a failing or sleeping cube is never CORS-masked. */
773
+ corsConfig?: {
774
+ /** @description Exact scheme://host[:port] origins (no path, query or trailing slash), or the single-element wildcard ["*"]. "*" and allowCredentials are mutually exclusive. */
775
+ allowedOrigins: string[];
776
+ /** @description Allowed methods. Omitted = GET, POST, PUT, PATCH, DELETE, OPTIONS. */
777
+ allowedMethods?: string[];
778
+ /** @description Allowed request headers. Omitted/empty = echo the preflight's Access-Control-Request-Headers. */
779
+ allowedHeaders?: string[];
780
+ /** @description Response headers exposed to JS. Omitted/empty = none. */
781
+ exposedHeaders?: string[];
782
+ /** @description Send Access-Control-Allow-Credentials: true. Rejected together with the "*" origin. */
783
+ allowCredentials?: boolean;
784
+ /** @description Preflight cache TTL. Default 600; 86400 is Chromium's hard cap. */
785
+ maxAgeSeconds?: number;
786
+ } | null;
787
+ /**
788
+ * @description Scheme the edge speaks to the CUBE on the backend hop. http (default) = cleartext. https = the cube terminates TLS itself (a control panel holding its own certificate, or an app listening on HTTPS) and answers plain HTTP with a redirect, so it cannot be reached over cleartext at all. Visitors are on HTTPS either way. The dial port is derived: https on the default port 80 connects on 443; a deliberate custom port is honoured exactly. Verified against the cube before it is applied — if the domain does not serve, the route is left on http.
789
+ * @enum {string}
790
+ */
791
+ originScheme?: "http" | "https";
772
792
  /** @description Edge gzip/zstd compression. Default false (domains behind a CDN are already compressed there). */
773
793
  responseCompression?: boolean;
774
794
  /** @description Visitor IP/CIDR allow-list (v4+v6). Non-empty ⇒ only these reach the app. Null/empty = open. */
@@ -885,6 +905,26 @@ interface paths {
885
905
  } | null;
886
906
  /** @description Max request body size (MB). Null = unlimited (default). */
887
907
  maxRequestBodyMb?: number | null;
908
+ /** @description Per-domain CORS policy, applied at the edge. Null (default) = CORS disabled and no Access-Control-* header is emitted anywhere. When set, the proxy answers OPTIONS preflight itself (204, the cube is never involved) and adds the headers to normal AND error responses, so a cross-origin request to a failing or sleeping cube is never CORS-masked. */
909
+ corsConfig?: {
910
+ /** @description Exact scheme://host[:port] origins (no path, query or trailing slash), or the single-element wildcard ["*"]. "*" and allowCredentials are mutually exclusive. */
911
+ allowedOrigins: string[];
912
+ /** @description Allowed methods. Omitted = GET, POST, PUT, PATCH, DELETE, OPTIONS. */
913
+ allowedMethods?: string[];
914
+ /** @description Allowed request headers. Omitted/empty = echo the preflight's Access-Control-Request-Headers. */
915
+ allowedHeaders?: string[];
916
+ /** @description Response headers exposed to JS. Omitted/empty = none. */
917
+ exposedHeaders?: string[];
918
+ /** @description Send Access-Control-Allow-Credentials: true. Rejected together with the "*" origin. */
919
+ allowCredentials?: boolean;
920
+ /** @description Preflight cache TTL. Default 600; 86400 is Chromium's hard cap. */
921
+ maxAgeSeconds?: number;
922
+ } | null;
923
+ /**
924
+ * @description Scheme the edge speaks to the CUBE on the backend hop. http (default) = cleartext. https = the cube terminates TLS itself (a control panel holding its own certificate, or an app listening on HTTPS) and answers plain HTTP with a redirect, so it cannot be reached over cleartext at all. Visitors are on HTTPS either way. The dial port is derived: https on the default port 80 connects on 443; a deliberate custom port is honoured exactly. Verified against the cube before it is applied — if the domain does not serve, the route is left on http.
925
+ * @enum {string}
926
+ */
927
+ originScheme?: "http" | "https";
888
928
  /** @description Edge gzip/zstd compression. Default false (domains behind a CDN are already compressed there). */
889
929
  responseCompression?: boolean;
890
930
  /** @description Visitor IP/CIDR allow-list (v4+v6). Non-empty ⇒ only these reach the app. Null/empty = open. */
@@ -1283,10 +1323,12 @@ interface paths {
1283
1323
  name: string;
1284
1324
  fileSizeBytes: number;
1285
1325
  /**
1326
+ * @description What to do with SSH access on the imported rootfs. `replace` (default) writes `sshPublicKey` to the login user's authorized_keys and requires `sshPublicKey` to be set. `keep` leaves the image's own authorized_keys and sshd configuration completely untouched — nothing is written — so you must already hold a private key that the image accepts, or you will not be able to log in.
1286
1327
  * @default replace
1287
1328
  * @enum {string}
1288
1329
  */
1289
1330
  sshKeyMode?: "replace" | "keep";
1331
+ /** @description Public key to install when `sshKeyMode` is `replace`. Written to the login user's `~/.ssh/authorized_keys` — which user that is comes from the imported rootfs itself, so an image without a Krova default user keeps logging in as root. Ignored when `sshKeyMode` is `keep`. */
1290
1332
  sshPublicKey?: string | null;
1291
1333
  region?: string | null;
1292
1334
  vcpusOverride?: number | null;
@@ -1726,6 +1768,7 @@ interface components {
1726
1768
  diskGb: number;
1727
1769
  };
1728
1770
  image: string;
1771
+ sshUser: string;
1729
1772
  costPerHour: number;
1730
1773
  /** Format: date-time */
1731
1774
  createdAt: string;
@@ -1748,6 +1791,7 @@ interface components {
1748
1791
  name: string;
1749
1792
  version: string;
1750
1793
  description: string;
1794
+ defaultUser: string;
1751
1795
  };
1752
1796
  PricingTier: {
1753
1797
  minVcpus: number;
@@ -1998,6 +2042,7 @@ declare class KrovaClient {
1998
2042
  diskGb: number;
1999
2043
  };
2000
2044
  image: string;
2045
+ sshUser: string;
2001
2046
  costPerHour: number;
2002
2047
  createdAt: string;
2003
2048
  updatedAt: string;
@@ -2189,6 +2234,7 @@ declare class KrovaClient {
2189
2234
  name: string;
2190
2235
  version: string;
2191
2236
  description: string;
2237
+ defaultUser: string;
2192
2238
  }[];
2193
2239
  }>;
2194
2240
  /** Per-resource hourly rates and volume pricing tiers. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krovacloud/sdk",
3
- "version": "0.3.9",
3
+ "version": "0.3.11",
4
4
  "description": "Official TypeScript SDK for Krova Cloud — a typed client for provisioning and managing Cubes (Firecracker microVMs) with dedicated resources.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -61,7 +61,7 @@
61
61
  "openapi-fetch": "^0.17.0"
62
62
  },
63
63
  "devDependencies": {
64
- "@types/node": "^26.1.1",
64
+ "@types/node": "^26.1.2",
65
65
  "openapi-typescript": "^7.13.0",
66
66
  "tsup": "^8.5.1",
67
67
  "tsx": "^4.23.1",