@hsuite/smart-engines-cli 1.2.1 → 1.4.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 +272 -58
- package/dist/_lib/attestation.d.ts +45 -0
- package/dist/_lib/attestation.d.ts.map +1 -0
- package/dist/_lib/attestation.js +76 -0
- package/dist/_lib/attestation.js.map +1 -0
- package/dist/_lib/baas.d.ts +147 -0
- package/dist/_lib/baas.d.ts.map +1 -0
- package/dist/_lib/baas.js +260 -0
- package/dist/_lib/baas.js.map +1 -0
- package/dist/_lib/cluster.d.ts +6 -6
- package/dist/_lib/cluster.js +6 -6
- package/dist/_lib/deploy-manifest.d.ts +69 -0
- package/dist/_lib/deploy-manifest.d.ts.map +1 -0
- package/dist/_lib/deploy-manifest.js +87 -0
- package/dist/_lib/deploy-manifest.js.map +1 -0
- package/dist/_lib/docker.d.ts +82 -0
- package/dist/_lib/docker.d.ts.map +1 -0
- package/dist/_lib/docker.js +164 -0
- package/dist/_lib/docker.js.map +1 -0
- package/dist/_lib/faucet-client.d.ts +64 -0
- package/dist/_lib/faucet-client.d.ts.map +1 -0
- package/dist/_lib/faucet-client.js +92 -0
- package/dist/_lib/faucet-client.js.map +1 -0
- package/dist/_lib/framework.d.ts +59 -0
- package/dist/_lib/framework.d.ts.map +1 -0
- package/dist/_lib/framework.js +149 -0
- package/dist/_lib/framework.js.map +1 -0
- package/dist/_lib/frontend-guard.d.ts +13 -0
- package/dist/_lib/frontend-guard.d.ts.map +1 -0
- package/dist/_lib/frontend-guard.js +41 -0
- package/dist/_lib/frontend-guard.js.map +1 -0
- package/dist/_lib/registry-fanout.d.ts +114 -0
- package/dist/_lib/registry-fanout.d.ts.map +1 -0
- package/dist/_lib/registry-fanout.js +435 -0
- package/dist/_lib/registry-fanout.js.map +1 -0
- package/dist/_lib/runtime-env.d.ts +126 -0
- package/dist/_lib/runtime-env.d.ts.map +1 -0
- package/dist/_lib/runtime-env.js +172 -0
- package/dist/_lib/runtime-env.js.map +1 -0
- package/dist/_lib/subscription-client.d.ts +129 -18
- package/dist/_lib/subscription-client.d.ts.map +1 -1
- package/dist/_lib/subscription-client.js +268 -40
- package/dist/_lib/subscription-client.js.map +1 -1
- package/dist/_lib/subscription-env.d.ts +35 -0
- package/dist/_lib/subscription-env.d.ts.map +1 -1
- package/dist/_lib/subscription-env.js +46 -2
- package/dist/_lib/subscription-env.js.map +1 -1
- package/dist/commands/app-lifecycle.d.ts +36 -0
- package/dist/commands/app-lifecycle.d.ts.map +1 -0
- package/dist/commands/app-lifecycle.js +145 -0
- package/dist/commands/app-lifecycle.js.map +1 -0
- package/dist/commands/ci.d.ts +45 -0
- package/dist/commands/ci.d.ts.map +1 -0
- package/dist/commands/ci.js +174 -0
- package/dist/commands/ci.js.map +1 -0
- package/dist/commands/deploy-manifest.d.ts +11 -0
- package/dist/commands/deploy-manifest.d.ts.map +1 -0
- package/dist/commands/deploy-manifest.js +17 -0
- package/dist/commands/deploy-manifest.js.map +1 -0
- package/dist/commands/deploy.d.ts +70 -11
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +263 -150
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/downgrade.js +1 -1
- package/dist/commands/downgrade.js.map +1 -1
- package/dist/commands/faucet.d.ts +78 -0
- package/dist/commands/faucet.d.ts.map +1 -0
- package/dist/commands/faucet.js +184 -0
- package/dist/commands/faucet.js.map +1 -0
- package/dist/commands/governance.js +1 -1
- package/dist/commands/governance.js.map +1 -1
- package/dist/commands/hist-balance.d.ts +1 -1
- package/dist/commands/hist-balance.js +1 -1
- package/dist/commands/init.js +1 -1
- package/dist/commands/list.d.ts +22 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +81 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/personhood.js +2 -2
- package/dist/commands/personhood.js.map +1 -1
- package/dist/commands/redeploy.d.ts +62 -0
- package/dist/commands/redeploy.d.ts.map +1 -0
- package/dist/commands/redeploy.js +310 -0
- package/dist/commands/redeploy.js.map +1 -0
- package/dist/commands/status.d.ts +51 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +138 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/subscribe.d.ts +94 -17
- package/dist/commands/subscribe.d.ts.map +1 -1
- package/dist/commands/subscribe.js +450 -81
- package/dist/commands/subscribe.js.map +1 -1
- package/dist/commands/transactions.d.ts +60 -0
- package/dist/commands/transactions.d.ts.map +1 -0
- package/dist/commands/transactions.js +101 -0
- package/dist/commands/transactions.js.map +1 -0
- package/dist/commands/update.d.ts +40 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +114 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/verify.d.ts +10 -19
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +14 -23
- package/dist/commands/verify.js.map +1 -1
- package/dist/index.d.ts +12 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -3
- package/dist/index.js.map +1 -1
- package/package.json +11 -4
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @hsuite/smart-engines-cli
|
|
2
2
|
|
|
3
3
|
`hsuite` — the developer bootstrap CLI for [Smart Engines V3](https://hsuite.finance).
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
Takes you from "empty directory" to "smart-app running against the live testnet
|
|
5
|
+
cluster" in under a minute, then manages the deployment's lifecycle.
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npm install -g @hsuite/smart-engines-cli
|
|
@@ -10,8 +10,14 @@ npm install -g @hsuite/smart-engines-cli
|
|
|
10
10
|
|
|
11
11
|
```
|
|
12
12
|
hsuite init # XRPL wallet + faucet + .env.local
|
|
13
|
-
hsuite subscribe --env <file> #
|
|
14
|
-
|
|
13
|
+
hsuite subscribe --env <file> # paid smart-app subscription on cluster (tier builder|growth|scale|enterprise)
|
|
14
|
+
|
|
15
|
+
# deployment lifecycle (manifest-driven):
|
|
16
|
+
hsuite deploy --manifest <f> # THE canonical path — fresh deploy AND redeploy
|
|
17
|
+
hsuite redeploy --manifest <f> # thin convenience: distinct --app-id / --tag override
|
|
18
|
+
hsuite update --replicas 3 # patch runtime config (cpu/memory/degree/replicas/env)
|
|
19
|
+
hsuite suspend | resume | delete
|
|
20
|
+
hsuite status [--watch] | list
|
|
15
21
|
```
|
|
16
22
|
|
|
17
23
|
XRPL-only. The CLI generates an XRPL keypair, funds it via the testnet
|
|
@@ -57,9 +63,8 @@ API_KEY_ENABLED=false
|
|
|
57
63
|
CORS_ORIGINS=http://localhost:8101,http://localhost:8100,http://localhost:4200
|
|
58
64
|
...
|
|
59
65
|
|
|
60
|
-
# Subscription — run `hsuite subscribe` to register a
|
|
61
|
-
|
|
62
|
-
SUBSCRIPTION_TIER=free_testnet
|
|
66
|
+
# Subscription — run `hsuite subscribe` to register a paid smart-app. Default tier is builder.
|
|
67
|
+
SUBSCRIPTION_TIER=builder
|
|
63
68
|
# SUBSCRIPTION_APP_ID= # populated by `hsuite subscribe`
|
|
64
69
|
```
|
|
65
70
|
|
|
@@ -67,13 +72,15 @@ SUBSCRIPTION_TIER=free_testnet
|
|
|
67
72
|
|
|
68
73
|
## `hsuite subscribe`
|
|
69
74
|
|
|
70
|
-
Register a
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
75
|
+
Register a paid smart-app subscription on the cluster. Every connection is
|
|
76
|
+
paid: pick a tier (`builder | growth | scale | enterprise`, default `builder`),
|
|
77
|
+
the cluster spins up a TSS deposit wallet and returns deposit instructions, and
|
|
78
|
+
the subscription NFT mints once the HSUITE deposit confirms on-chain. DKG runs
|
|
79
|
+
so the cluster-issued `appId` becomes your smart-app's on-chain identity.
|
|
74
80
|
|
|
75
81
|
```bash
|
|
76
82
|
hsuite subscribe --env .env.local
|
|
83
|
+
hsuite subscribe --env .env.local --tier growth
|
|
77
84
|
hsuite subscribe --env .env.local --name "My Smart App"
|
|
78
85
|
hsuite subscribe --env .env.local --services database,storage,messaging
|
|
79
86
|
hsuite subscribe --env .env.local --gateway https://my-cluster.example.com
|
|
@@ -85,52 +92,263 @@ Flow:
|
|
|
85
92
|
2. Builds a signer using `ripple-keypairs.sign`
|
|
86
93
|
3. Authenticates via `BaasClient.authenticate({chain: 'xrpl', ...})` against the cluster
|
|
87
94
|
4. Calls `BaasClient.deployment.init({name, port, services})` — the new four-step deploy flow's step 1 (legacy `register()` removed in PR-A); the cluster runs the per-entity DKG ceremony and returns the DKG entityId as `appId`
|
|
88
|
-
5. Appends `SUBSCRIPTION_APP_ID=<appId>` and `APP_ID=<appId>` to your `.env.local
|
|
95
|
+
5. Appends `SUBSCRIPTION_APP_ID=<appId>` and `APP_ID=<appId>` to your `.env.local`
|
|
89
96
|
|
|
90
97
|
After this, your smart-app boots in cluster-mode automatically.
|
|
91
98
|
|
|
92
99
|
---
|
|
93
100
|
|
|
94
|
-
##
|
|
101
|
+
## Deployment commands
|
|
95
102
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
`uploadFrontend` → `deploy`). Use this for CI pipelines that ship a
|
|
99
|
-
container image to the cluster.
|
|
103
|
+
Eight commands take a smart-app from a manifest to a running, manageable
|
|
104
|
+
deployment on the cluster. They all share base flags:
|
|
100
105
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
106
|
+
| Flag | Default | Meaning |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| `--env <file>` | `.env.local` | Env file with the XRPL auth creds |
|
|
109
|
+
| `--gateway <url>` | `https://gateway.testnet.hsuite.network` | Public gateway/host URL |
|
|
110
|
+
| `--app-id <id>` | `DEPLOYED_APP_ID` → `SUBSCRIPTION_APP_ID` | App to act on |
|
|
111
|
+
|
|
112
|
+
Every command constructs exactly **one** `BaasClient`
|
|
113
|
+
(`{ hostUrl, appId, pathPrefix: '/host', allowInsecure, timeout }`) and
|
|
114
|
+
authenticates it with the env XRPL signer — connecting straight to the public
|
|
115
|
+
round-robin gateway (no cluster-discovery factory, whose per-cluster hostnames
|
|
116
|
+
aren't reachable from a dev machine).
|
|
117
|
+
|
|
118
|
+
### The manifest (one canonical shape)
|
|
105
119
|
|
|
106
|
-
`
|
|
120
|
+
Source of truth: `packages/hsuite-cli/src/_lib/deploy-manifest.ts`.
|
|
107
121
|
|
|
108
|
-
|
|
122
|
+
Every smart-app is a **NestJS backend**, optionally fronted by an **ionic SPA**.
|
|
123
|
+
The manifest has ONE shape: a required `backend` block plus an optional
|
|
124
|
+
`frontend` block.
|
|
125
|
+
|
|
126
|
+
```jsonc
|
|
109
127
|
{
|
|
110
128
|
"name": "my-smart-app",
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
"
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
129
|
+
"services": ["database", "storage", "messaging", "functions"], // ('auth'|'database'|'storage'|'functions'|'messaging')[]
|
|
130
|
+
"degree": 3, // clusters to span (>=3; 3 is the floor)
|
|
131
|
+
"backend": { // REQUIRED — the NestJS image
|
|
132
|
+
"port": 3200, // backend listen port, 1..65535, NEVER 8080 (reserved for the edge sidecar)
|
|
133
|
+
"tag": "v1", // image tag (REQUIRED)
|
|
134
|
+
"replicas": 1, // pods per cluster (default 1)
|
|
135
|
+
"context": ".", // docker build context (default '.')
|
|
136
|
+
"dockerfile": "Dockerfile", // default 'Dockerfile'
|
|
137
|
+
"platform": "linux/amd64", // default 'linux/amd64' (cluster-node arch)
|
|
138
|
+
"build": "yarn build", // optional pre-build shell cmd, run in context
|
|
139
|
+
"env": { "NODE_ENV": "production" }, // NON-SECRET overrides only — merged over the auto-forwarded .env.local runtime env (manifest wins)
|
|
140
|
+
"limits": { "cpu": "500m", "memory": "512Mi" }
|
|
141
|
+
},
|
|
142
|
+
"frontend": { // OPTIONAL — ionic SPA, served by the EDGE sidecar (NOT the backend)
|
|
143
|
+
"build": "ng build", // optional pre-build command
|
|
144
|
+
"dir": "www", // dir to tar into the SPA bundle
|
|
145
|
+
"bundlePath": null // OR a pre-built tarball (wins over `dir`)
|
|
146
|
+
}
|
|
117
147
|
}
|
|
118
148
|
```
|
|
119
149
|
|
|
120
|
-
|
|
150
|
+
#### Runtime env: auto-forwarded from `.env.local` (do NOT hand-copy secrets)
|
|
151
|
+
|
|
152
|
+
`hsuite deploy` **automatically forwards your `.env.local` runtime creds into the
|
|
153
|
+
deployed container** — so the deployed app runs with the **same identity + config
|
|
154
|
+
it runs with locally**. The deployed smart-app authenticates as **your own XRPL
|
|
155
|
+
wallet** (the `XRPL_SEED` in `.env.local`, whose address holds the subscription
|
|
156
|
+
NFT); there is no separate app identity. You therefore **never hand-copy
|
|
157
|
+
`XRPL_SEED` / `HEDERA_PRIVATE_KEY` / chain creds into a committed manifest** — a
|
|
158
|
+
manual step that, if forgotten, silently shipped a **credential-less, open-mode**
|
|
159
|
+
app (`/baas/status` → `{"mode":"memory","appId":null}`; BaaS routes 503). See
|
|
160
|
+
issue #1564.
|
|
161
|
+
|
|
162
|
+
**What gets forwarded:** the runtime-relevant keys from your `.env.local` — the
|
|
163
|
+
same set the smart-app's env schema reads (`XRPL_*`, `SUBSCRIPTION_APP_ID`,
|
|
164
|
+
`APP_*`, `VALIDATOR_URL`/`SMART_HOST_URL`, AI/Discord config, …). Purely
|
|
165
|
+
deploy-time / CLI-only knobs (e.g. `SUBSCRIPTION_NFT_SERIAL`, `VALIDATOR_API_KEY`)
|
|
166
|
+
are **not** forwarded. The list lives in
|
|
167
|
+
`packages/hsuite-cli/src/_lib/runtime-env.ts` (`RUNTIME_ENV_KEYS`), mirroring the
|
|
168
|
+
smart-app schema (`apps/showcase/smart-app/src/config/env-validation.ts` in the
|
|
169
|
+
hsuite-ecosystem repo).
|
|
170
|
+
|
|
171
|
+
**Precedence:** the forwarded `.env.local` values are the base;
|
|
172
|
+
**`manifest.backend.env` is overlaid last and WINS** for any key it sets. Use
|
|
173
|
+
`backend.env` for explicit **non-secret** per-deploy overrides (ports, feature
|
|
174
|
+
flags, Discord/AI endpoints). Secrets flow automatically from `.env.local` and do
|
|
175
|
+
not belong in a committed manifest. Everything lands in the app's **PQC-encrypted
|
|
176
|
+
(kyber-aes-v1) per-app customer-env Secret** — the pod's runtime env, `envFrom`-
|
|
177
|
+
mounted on the backend container. Encryption at rest is the containment boundary;
|
|
178
|
+
no secret is ever written into a committed file.
|
|
179
|
+
|
|
180
|
+
**Fail-loud:** if a deploy would ship the app **credential-less** (no `XRPL_SEED`
|
|
181
|
+
reaching the container) the CLI **errors** rather than silently booting open mode.
|
|
182
|
+
An intentional open-mode deploy stays valid via the explicit `--allow-open-mode`
|
|
183
|
+
flag, which deliberately **withholds** the seed from the container (open mode)
|
|
184
|
+
while still using it locally to sign the required build attestation.
|
|
185
|
+
|
|
186
|
+
> The same forwarding + fail-loud applies to `hsuite redeploy`.
|
|
187
|
+
|
|
188
|
+
Backend-only example (no `frontend` block — the edge proxies all paths to the
|
|
189
|
+
backend):
|
|
190
|
+
|
|
191
|
+
```jsonc
|
|
192
|
+
{
|
|
193
|
+
"name": "my-api",
|
|
194
|
+
"services": ["database"],
|
|
195
|
+
"degree": 3,
|
|
196
|
+
"backend": { "port": 3200, "tag": "v1" }
|
|
197
|
+
}
|
|
198
|
+
```
|
|
121
199
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
200
|
+
> Relative `frontend.dir` / `frontend.bundlePath` (and the frontend `build` hook)
|
|
201
|
+
> resolve against the backend's build `context` (the smart-app root) — NOT the
|
|
202
|
+
> directory you ran `hsuite` from. A bare `"public"` means
|
|
203
|
+
> `<smart-app>/public`. `degree` is validated client-side against the host floor
|
|
204
|
+
> (`>= 3`) so a bad value fails with a clear CLI error instead of a server 500.
|
|
205
|
+
|
|
206
|
+
### Serving model: the public-vs-edge split
|
|
207
|
+
|
|
208
|
+
Every deployed customer pod has **two containers**:
|
|
209
|
+
|
|
210
|
+
- The customer **backend** listens on **loopback** at `backend.port` (e.g.
|
|
211
|
+
`3200`). It is NOT exposed at the pod boundary, and `8080` is **forbidden**
|
|
212
|
+
for the backend.
|
|
213
|
+
- A platform **edge sidecar** (Caddy) listens on port `8080` — the pod's
|
|
214
|
+
exposed/Service port. The edge reverse-proxies API requests to the backend on
|
|
215
|
+
loopback and serves the SPA.
|
|
216
|
+
|
|
217
|
+
The ionic SPA is served by the **edge sidecar, not the backend**. The optional
|
|
218
|
+
`frontend` block is tarred and uploaded (`uploadFrontend`) into the cluster BaaS
|
|
219
|
+
storage tier, content-addressed by sha256. At pod start a **bundle-fetcher init
|
|
220
|
+
container** fetches that bundle into a shared emptyDir, which the edge serves
|
|
221
|
+
read-only from `/srv/spa`, falling back to the SPA shell (`index.html`) for
|
|
222
|
+
client-side routing. For a **backend-only** app (no `frontend` block) the edge
|
|
223
|
+
proxies all paths to the backend.
|
|
224
|
+
|
|
225
|
+
> The SPA is also commonly baked into the backend image's own `public/` — but in
|
|
226
|
+
> the deployed pod it is the **edge-served uploaded bundle** that the public sees
|
|
227
|
+
> on port `8080`.
|
|
228
|
+
>
|
|
229
|
+
> (Refs: `apps/smart-deployer/src/reconciler/caddyfile-renderer.service.ts`,
|
|
230
|
+
> `apps/smart-deployer/src/reconciler/k8s-resource-builder.service.ts`.)
|
|
231
|
+
|
|
232
|
+
### Multi-cluster image distribution
|
|
233
|
+
|
|
234
|
+
`harbor.testnet.hsuite.network` is **public-DNS round-robin** over the 3 cluster
|
|
235
|
+
ingress IPs, each fronting its own Harbor. A single `docker push` lands the image
|
|
236
|
+
on **only one** cluster. `hsuite deploy` therefore **fans the image out** to every
|
|
237
|
+
cluster Harbor itself, synchronously, BEFORE reconciling: it resolves the
|
|
238
|
+
registry host's A-records, copies the pushed image to each cluster at the
|
|
239
|
+
Registry-v2 level (TLS-SNI pinned to the federated hostname, 401/Bearer token
|
|
240
|
+
auth, blob copy + manifest PUT), then **re-verifies the exact pushed digest is
|
|
241
|
+
served by every cluster and ABORTS the deploy** if any cluster can't be made to
|
|
242
|
+
serve it. This replaces reliance on the (unreliable) Harbor cross-cluster
|
|
243
|
+
replication mesh for customer images — the mesh is no longer load-bearing. The
|
|
244
|
+
push-robot credential is BLS-deterministic and identical on all 3 cluster
|
|
245
|
+
Harbors, which is what lets one credential authenticate the fan-out against every
|
|
246
|
+
cluster. `--skip-distribute` opts out (single-cluster / dev registry).
|
|
247
|
+
(Implementation: `packages/hsuite-cli/src/_lib/registry-fanout.ts`.)
|
|
248
|
+
|
|
249
|
+
### Deploy strategy (Recreate vs RollingUpdate)
|
|
250
|
+
|
|
251
|
+
The strategy is driven by replica count
|
|
252
|
+
(`apps/smart-deployer/src/reconciler/k8s-resource-builder.service.ts`
|
|
253
|
+
`buildDeployment`):
|
|
254
|
+
|
|
255
|
+
- **Single-replica apps (`replicas <= 1`) use `Recreate`.** A single-replica
|
|
256
|
+
tenant CANNOT surge — the per-tenant `ResourceQuota` only fits one pod — so
|
|
257
|
+
`RollingUpdate` would deadlock. Pods are stateless (no RWO PVC), so
|
|
258
|
+
Recreate-in-place is safe.
|
|
259
|
+
- **Multi-replica apps use zero-downtime `RollingUpdate{maxSurge:1,
|
|
260
|
+
maxUnavailable:0}`.**
|
|
261
|
+
|
|
262
|
+
The `strategy` field on the manifest/deploy request is **advisory**: a
|
|
263
|
+
`recreate` request is coerced to `RollingUpdate` for multi-replica apps (with a
|
|
264
|
+
warning).
|
|
265
|
+
|
|
266
|
+
### `hsuite deploy` — THE canonical deploy path
|
|
267
|
+
|
|
268
|
+
`hsuite deploy --manifest <file>` is the **single, canonical path** to deploy a
|
|
269
|
+
customer smart-app (NestJS backend + optional ionic SPA) across the 3-cluster
|
|
270
|
+
testnet. It handles BOTH a fresh deploy AND a redeploy of an existing/active
|
|
271
|
+
app. There is no hand-rolled `deploy-*.cjs` script and no manual per-cluster
|
|
272
|
+
`docker push` — exactly one path: `hsuite deploy`.
|
|
273
|
+
|
|
274
|
+
**Mint-first / deploy-last:** the developer's `SUBSCRIPTION_APP_ID` IS the deploy
|
|
275
|
+
target (created + paid by `hsuite subscribe`; `SUBSCRIPTION_APP_ID ==
|
|
276
|
+
DEPLOYED_APP_ID == DKG entityId`). `deploy` **allocates nothing** — it
|
|
277
|
+
authenticates as that id and fetches push creds for it (`pushCredentials`,
|
|
278
|
+
idempotent).
|
|
279
|
+
|
|
280
|
+
Flow: resolve `SUBSCRIPTION_APP_ID` → `connectAndAuth` → `pushCredentials` →
|
|
281
|
+
docker build+push (backend) → **fan the image out to every cluster Harbor**
|
|
282
|
+
(verify + abort on any miss) → tar + `uploadFrontend` (frontend) → sign a build
|
|
283
|
+
attestation → `deploy()`. Writes `DEPLOYED_APP_ID` to the env file.
|
|
132
284
|
|
|
133
|
-
|
|
285
|
+
```bash
|
|
286
|
+
hsuite deploy --manifest ./my-app.json --env .env.local
|
|
287
|
+
hsuite deploy --manifest ./my-app.json --strategy recreate
|
|
288
|
+
hsuite deploy --manifest ./my-app.json --docker-host unix:///path/docker.sock
|
|
289
|
+
hsuite deploy --manifest ./my-app.json --skip-build --skip-push # image already pushed
|
|
290
|
+
hsuite deploy --manifest ./my-app.json --skip-distribute # single-cluster / dev registry
|
|
291
|
+
hsuite deploy --manifest ./my-app.json --subscription-app-id sub_… # override the deploy target
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Flags: `--manifest <file>`, `--env <file>` (default `.env.local`), `--project
|
|
295
|
+
<dir>` (SDK/framework detection root), `--gateway <url>`, `--subscription-app-id
|
|
296
|
+
<id>`, `--strategy rolling|recreate`, `--docker-host <host>` (defaults to
|
|
297
|
+
`$DOCKER_HOST`), `--skip-build`, `--skip-push`, `--skip-distribute`,
|
|
298
|
+
`--allow-open-mode` (permit an intentional zero-cred / open-mode deploy; default
|
|
299
|
+
OFF — a subscribed-but-credential-less deploy fails loud), `--non-interactive`.
|
|
300
|
+
|
|
301
|
+
The docker login uses the init-issued one-time robot creds via
|
|
302
|
+
`execFileSync('docker', ['login', server, '-u', username, '--password-stdin'])`
|
|
303
|
+
— the robot username (`robot$project+name`) is a discrete argv element and the
|
|
304
|
+
password is fed on stdin, so the `$` is never shell-expanded.
|
|
305
|
+
|
|
306
|
+
### `hsuite redeploy` — thin convenience
|
|
307
|
+
|
|
308
|
+
`redeploy` is NOT a separate required path — `hsuite deploy` already redeploys an
|
|
309
|
+
existing/active app. `redeploy` is a thin convenience for passing a **distinct
|
|
310
|
+
deployed `--app-id`** and/or a **`--tag` override**. It rebuilds + pushes the
|
|
311
|
+
backend image, fans it out to every cluster (same `--skip-distribute` opt-out),
|
|
312
|
+
re-uploads the SPA bundle when the manifest carries a `frontend` block, then
|
|
313
|
+
`deploy()`s.
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
hsuite redeploy --manifest ./my-app.json --tag v2
|
|
317
|
+
hsuite redeploy --manifest ./my-app.json --app-id app_… --strategy recreate
|
|
318
|
+
hsuite redeploy --manifest ./my-app.json --skip-distribute
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### `hsuite update`
|
|
322
|
+
|
|
323
|
+
Patch runtime config without a new image — at least one field required.
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
hsuite update --cpu 500m --memory 512Mi
|
|
327
|
+
hsuite update --replicas 3 --degree 3
|
|
328
|
+
hsuite update --strategy recreate
|
|
329
|
+
hsuite update --env-set NODE_ENV=production --env-set LOG_LEVEL=info
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### `hsuite suspend` / `resume` / `delete`
|
|
333
|
+
|
|
334
|
+
```bash
|
|
335
|
+
hsuite suspend # scale the runtime to zero
|
|
336
|
+
hsuite resume # scale it back up
|
|
337
|
+
hsuite delete # tear down (confirms; --yes to skip, required when non-TTY)
|
|
338
|
+
hsuite delete --yes
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### `hsuite status` / `list`
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
hsuite status # state + runtime (runtimeState / replicas / lastError)
|
|
345
|
+
hsuite status --watch # poll until RUNNING / FAILED
|
|
346
|
+
hsuite status --json
|
|
347
|
+
hsuite list # table of {appId, name, status}
|
|
348
|
+
hsuite list --json
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
The deployed `appId` is appended to `.env.local` as `DEPLOYED_APP_ID`.
|
|
134
352
|
|
|
135
353
|
---
|
|
136
354
|
|
|
@@ -141,7 +359,7 @@ mkdir my-smart-app && cd my-smart-app
|
|
|
141
359
|
npm install -g @hsuite/smart-engines-cli
|
|
142
360
|
|
|
143
361
|
hsuite init # → .env.local with funded XRPL wallet
|
|
144
|
-
hsuite subscribe --env .env.local # →
|
|
362
|
+
hsuite subscribe --env .env.local # → paid (builder) subscription appId on cluster
|
|
145
363
|
cat .env.local | grep XRPL_ADDRESS # → your wallet address
|
|
146
364
|
cat .env.local | grep SUBSCRIPTION_APP_ID # → your smart-app's DKG entity ID
|
|
147
365
|
```
|
|
@@ -156,30 +374,26 @@ That's it — you now have:
|
|
|
156
374
|
## What the CLI does NOT do
|
|
157
375
|
|
|
158
376
|
- **Pay HCS fees.** The cluster's validator/host pods do that from their own pod-local Hedera operator accounts. The smart-app never sees Hedera credentials.
|
|
159
|
-
- **
|
|
377
|
+
- **Confirm the HSUITE deposit for you.** `subscribe` creates the subscription and returns deposit instructions, but you fund the TSS deposit wallet yourself; the subscription NFT mints (and the smart-app goes ACTIVE) only once that deposit confirms on-chain.
|
|
160
378
|
- **Configure your smart-app's business logic.** If your smart-app does Hedera-side or Solana-side or any other-chain ops as part of its app logic, that's separate from the dev wallet and you configure it in your app's own code/env.
|
|
161
379
|
|
|
162
380
|
---
|
|
163
381
|
|
|
164
382
|
## Use programmatically
|
|
165
383
|
|
|
166
|
-
|
|
384
|
+
The `hsuite` package is a **CLI only** — its entry point (`dist/index.js`) is an executable that parses `process.argv` on load and exposes no importable API. There are no `initWallet` / `subscribeFreeTestnet` / `deploySmartApp` functions to import.
|
|
385
|
+
|
|
386
|
+
For programmatic access from your own code, use the SDK directly — see [Companion packages](#companion-packages):
|
|
167
387
|
|
|
168
388
|
```ts
|
|
169
|
-
import {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
await subscribeFreeTestnet({
|
|
179
|
-
env: '.env.local',
|
|
180
|
-
gateway: 'https://v3-testnet-gateway.hsuite.network',
|
|
181
|
-
services: 'database,storage,messaging,functions',
|
|
182
|
-
});
|
|
389
|
+
import { /* ... */ } from '@hsuite/smart-engines-sdk';
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
To automate the CLI itself, shell out to the `hsuite` binary from your build scripts or CI:
|
|
393
|
+
|
|
394
|
+
```sh
|
|
395
|
+
hsuite init --out .env.local
|
|
396
|
+
hsuite subscribe --env .env.local --tier builder
|
|
183
397
|
```
|
|
184
398
|
|
|
185
399
|
---
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type BuildAttestation } from '@hsuite/smart-engines-sdk';
|
|
2
|
+
import type { Framework } from './framework';
|
|
3
|
+
/** Everything needed to assemble an attestation, minus `version`/`builtAt`. */
|
|
4
|
+
export type AttestationInputs = {
|
|
5
|
+
appId: string;
|
|
6
|
+
developerWallet: string;
|
|
7
|
+
developerChain: BuildAttestation['developerChain'];
|
|
8
|
+
framework: Framework;
|
|
9
|
+
sdkVersion: string;
|
|
10
|
+
imageDigest: string;
|
|
11
|
+
/** '' for backend-only apps (no frontend bundle). */
|
|
12
|
+
bundleSha256: string;
|
|
13
|
+
cliVersion: string;
|
|
14
|
+
/** Injectable clock for deterministic tests; defaults to `Date.now()`. */
|
|
15
|
+
now?: () => number;
|
|
16
|
+
};
|
|
17
|
+
/** A signed attestation: the payload plus the developer's `<pub>.<sig>` string. */
|
|
18
|
+
export type SignedAttestation = {
|
|
19
|
+
payload: BuildAttestation;
|
|
20
|
+
signature: string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Read this CLI's own version from its `package.json`, for `cliVersion`.
|
|
24
|
+
*
|
|
25
|
+
* `__dirname` at runtime is `dist/_lib`, so the package root is two levels up.
|
|
26
|
+
* Falls back to `'0.0.0'` if it can't be read (mirrors index.ts's getVersion).
|
|
27
|
+
*/
|
|
28
|
+
export declare function readCliVersion(): string;
|
|
29
|
+
/**
|
|
30
|
+
* Assemble a {@link BuildAttestation} from resolved inputs. Pins `version: '1'`
|
|
31
|
+
* and stamps `builtAt` from the injectable clock (default `Date.now()`).
|
|
32
|
+
*
|
|
33
|
+
* Field order here is cosmetic — the SDK canonicalizer sorts keys before
|
|
34
|
+
* hashing — but mirrors the type for readability.
|
|
35
|
+
*/
|
|
36
|
+
export declare function assembleAttestation(inputs: AttestationInputs): BuildAttestation;
|
|
37
|
+
/**
|
|
38
|
+
* Assemble + sign in one step with the developer's XRPL seed, returning the
|
|
39
|
+
* `{ payload, signature }` envelope the deploy request carries.
|
|
40
|
+
*
|
|
41
|
+
* Delegates the signature to the frozen SDK primitive so the bytes the server
|
|
42
|
+
* verifies are produced by the canonical code path.
|
|
43
|
+
*/
|
|
44
|
+
export declare function assembleAndSign(inputs: AttestationInputs, xrplSeed: string): SignedAttestation;
|
|
45
|
+
//# sourceMappingURL=attestation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attestation.d.ts","sourceRoot":"","sources":["../../src/_lib/attestation.ts"],"names":[],"mappings":"AAmBA,OAAO,EACL,KAAK,gBAAgB,EAEtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,+EAA+E;AAC/E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;IACnD,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB,CAAC;AAEF,mFAAmF;AACnF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAQvC;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,iBAAiB,GAAG,gBAAgB,CAc/E;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,iBAAiB,EACzB,QAAQ,EAAE,MAAM,GACf,iBAAiB,CAInB"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readCliVersion = readCliVersion;
|
|
4
|
+
exports.assembleAttestation = assembleAttestation;
|
|
5
|
+
exports.assembleAndSign = assembleAndSign;
|
|
6
|
+
/**
|
|
7
|
+
* CLI-side assembly of a {@link BuildAttestation}.
|
|
8
|
+
*
|
|
9
|
+
* The cryptographic primitive (`signAttestation`, the `BuildAttestation` type,
|
|
10
|
+
* the canonicalizer) lives in `@hsuite/smart-engines-sdk` and is FROZEN — this
|
|
11
|
+
* module only imports it. The SDK primitive is deliberately clock-free and
|
|
12
|
+
* pure; the one impure input, `builtAt`, is supplied HERE (the CLI is allowed
|
|
13
|
+
* to read the clock).
|
|
14
|
+
*
|
|
15
|
+
* `assembleAttestation` gathers every field from already-resolved inputs:
|
|
16
|
+
* - developerWallet / developerChain : the env XRPL identity (env.ts signer)
|
|
17
|
+
* - sdkVersion : installed/declared SDK (framework.ts)
|
|
18
|
+
* - cliVersion : this CLI's package.json version
|
|
19
|
+
* - imageDigest : the pushed image's manifest digest
|
|
20
|
+
* - bundleSha256 : uploadFrontend's server-computed hash
|
|
21
|
+
* - builtAt : Date.now() at call time
|
|
22
|
+
*/
|
|
23
|
+
const fs_1 = require("fs");
|
|
24
|
+
const path_1 = require("path");
|
|
25
|
+
const smart_engines_sdk_1 = require("@hsuite/smart-engines-sdk");
|
|
26
|
+
/**
|
|
27
|
+
* Read this CLI's own version from its `package.json`, for `cliVersion`.
|
|
28
|
+
*
|
|
29
|
+
* `__dirname` at runtime is `dist/_lib`, so the package root is two levels up.
|
|
30
|
+
* Falls back to `'0.0.0'` if it can't be read (mirrors index.ts's getVersion).
|
|
31
|
+
*/
|
|
32
|
+
function readCliVersion() {
|
|
33
|
+
try {
|
|
34
|
+
const pkgPath = (0, path_1.join)(__dirname, '..', '..', 'package.json');
|
|
35
|
+
const pkg = JSON.parse((0, fs_1.readFileSync)(pkgPath, 'utf8'));
|
|
36
|
+
return pkg.version ?? '0.0.0';
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return '0.0.0';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Assemble a {@link BuildAttestation} from resolved inputs. Pins `version: '1'`
|
|
44
|
+
* and stamps `builtAt` from the injectable clock (default `Date.now()`).
|
|
45
|
+
*
|
|
46
|
+
* Field order here is cosmetic — the SDK canonicalizer sorts keys before
|
|
47
|
+
* hashing — but mirrors the type for readability.
|
|
48
|
+
*/
|
|
49
|
+
function assembleAttestation(inputs) {
|
|
50
|
+
const now = inputs.now ?? Date.now;
|
|
51
|
+
return {
|
|
52
|
+
version: '1',
|
|
53
|
+
appId: inputs.appId,
|
|
54
|
+
developerWallet: inputs.developerWallet,
|
|
55
|
+
developerChain: inputs.developerChain,
|
|
56
|
+
framework: inputs.framework,
|
|
57
|
+
sdkVersion: inputs.sdkVersion,
|
|
58
|
+
imageDigest: inputs.imageDigest,
|
|
59
|
+
bundleSha256: inputs.bundleSha256,
|
|
60
|
+
cliVersion: inputs.cliVersion,
|
|
61
|
+
builtAt: now(),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Assemble + sign in one step with the developer's XRPL seed, returning the
|
|
66
|
+
* `{ payload, signature }` envelope the deploy request carries.
|
|
67
|
+
*
|
|
68
|
+
* Delegates the signature to the frozen SDK primitive so the bytes the server
|
|
69
|
+
* verifies are produced by the canonical code path.
|
|
70
|
+
*/
|
|
71
|
+
function assembleAndSign(inputs, xrplSeed) {
|
|
72
|
+
const payload = assembleAttestation(inputs);
|
|
73
|
+
const signature = (0, smart_engines_sdk_1.signAttestation)(payload, xrplSeed);
|
|
74
|
+
return { payload, signature };
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=attestation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attestation.js","sourceRoot":"","sources":["../../src/_lib/attestation.ts"],"names":[],"mappings":";;AAoDA,wCAQC;AASD,kDAcC;AASD,0CAOC;AAnGD;;;;;;;;;;;;;;;;GAgBG;AACH,2BAAkC;AAClC,+BAA4B;AAC5B,iEAGmC;AAwBnC;;;;;GAKG;AACH,SAAgB,cAAc;IAC5B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAA,WAAI,EAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;QAC5D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAA,iBAAY,EAAC,OAAO,EAAE,MAAM,CAAC,CAAyB,CAAC;QAC9E,OAAO,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,mBAAmB,CAAC,MAAyB;IAC3D,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IACnC,OAAO;QACL,OAAO,EAAE,GAAG;QACZ,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,eAAe,EAAE,MAAM,CAAC,eAAe;QACvC,cAAc,EAAE,MAAM,CAAC,cAAc;QACrC,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,OAAO,EAAE,GAAG,EAAE;KACf,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,eAAe,CAC7B,MAAyB,EACzB,QAAgB;IAEhB,MAAM,OAAO,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,IAAA,mCAAkB,EAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACxD,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AAChC,CAAC"}
|