@harperfast/template-react-ts-studio 1.12.8 → 1.12.9

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 (26) hide show
  1. package/.agents/skills/harper-best-practices/AGENTS.md +514 -445
  2. package/.agents/skills/harper-best-practices/rules/automatic-apis.md +96 -93
  3. package/.agents/skills/harper-best-practices/rules/deploying-to-harper-fabric.md +84 -58
  4. package/.agents/skills/harper-best-practices/rules/extending-tables.md +27 -35
  5. package/.agents/skills/harper-best-practices/rules/handling-binary-data.md +40 -39
  6. package/.agents/skills/harper-best-practices/rules/querying-rest-apis.md +93 -37
  7. package/.agents/skills/harper-best-practices/rules/schema-design-tooling.md +68 -66
  8. package/.agents/skills/harper-best-practices/rules/using-blob-datatype.md +24 -28
  9. package/.agents/skills/harper-best-practices/rules/v5-upgrade.md +98 -105
  10. package/.agents/skills/harper-best-practices/rules.manifest.yaml +28 -3
  11. package/.agents/skills/harper-mcp/AGENTS.md +138 -26
  12. package/.agents/skills/harper-mcp/rules/enabling-mcp.md +140 -28
  13. package/agent/skills/harper-best-practices/AGENTS.md +514 -445
  14. package/agent/skills/harper-best-practices/rules/automatic-apis.md +96 -93
  15. package/agent/skills/harper-best-practices/rules/deploying-to-harper-fabric.md +84 -58
  16. package/agent/skills/harper-best-practices/rules/extending-tables.md +27 -35
  17. package/agent/skills/harper-best-practices/rules/handling-binary-data.md +40 -39
  18. package/agent/skills/harper-best-practices/rules/querying-rest-apis.md +93 -37
  19. package/agent/skills/harper-best-practices/rules/schema-design-tooling.md +68 -66
  20. package/agent/skills/harper-best-practices/rules/using-blob-datatype.md +24 -28
  21. package/agent/skills/harper-best-practices/rules/v5-upgrade.md +98 -105
  22. package/agent/skills/harper-best-practices/rules.manifest.yaml +28 -3
  23. package/agent/skills/harper-mcp/AGENTS.md +138 -26
  24. package/agent/skills/harper-mcp/rules/enabling-mcp.md +140 -28
  25. package/package.json +1 -1
  26. package/skills-lock.json +2 -2
@@ -12,8 +12,8 @@ metadata:
12
12
  - reference/v5/rest/overview.md#Content Types
13
13
  - reference/v5/rest/overview.md#OpenAPI
14
14
  - reference/v5/rest/websockets.md
15
- sourceCommit: 677ad213d67822e109c83619e181ca23a59823db
16
- inputHash: a9356e92dd3dc106
15
+ sourceCommit: 2cfb81318f17e0aca2d600109e6ad813d2d0443e
16
+ inputHash: fc3e7ae596f475b2
17
17
  ---
18
18
 
19
19
  # Automatic APIs
@@ -22,25 +22,26 @@ Instructions for the agent to follow when using Harper's automatically generated
22
22
 
23
23
  ## When to Use
24
24
 
25
- Apply this rule when enabling HTTP REST endpoints or WebSocket subscriptions for Harper tables without writing custom handler code. Use it whenever a schema type needs to be served over HTTP, when configuring real-time subscriptions, or when setting up conditional caching behavior for REST responses.
25
+ Apply this rule when enabling HTTP REST endpoints or WebSocket subscriptions for Harper tables and custom resources. Use it whenever you need to configure the REST plugin, understand the auto-generated endpoint surface, or wire up real-time WebSocket connections. See [querying-rest-apis.md](querying-rest-apis.md) for query syntax and [real-time-apps.md](real-time-apps.md) for real-time patterns.
26
26
 
27
27
  ## How It Works
28
28
 
29
- 1. **Enable REST in `config.yaml`**: Add `rest: true` to the application configuration file. This registers REST endpoints and, by default, WebSocket subscriptions for all exported resources.
29
+ 1. **Enable the REST plugin** by adding `rest: true` to your application's `config.yaml`:
30
30
 
31
31
  ```yaml
32
32
  rest: true
33
33
  ```
34
34
 
35
- To configure options explicitly:
35
+ Extended options:
36
36
 
37
37
  ```yaml
38
38
  rest:
39
39
  lastModified: true # enables Last-Modified response header support
40
40
  webSocket: false # disables automatic WebSocket support (enabled by default)
41
+ exactCount: true # opt in to Prefer: count=exact scans (off by default)
41
42
  ```
42
43
 
43
- 2. **Export the table in the schema**: Add `@export` to the type definition. Without `@export`, Harper registers no REST route and callers receive `404`. Without `rest: true`, even an exported table does not respond to HTTP requests. Both are required.
44
+ 2. **Export the table in your schema** using `@export`. Tables are not exposed by default — `@export` is required for Harper to register a REST route:
44
45
 
45
46
  ```graphql
46
47
  type Product @table @export {
@@ -50,46 +51,41 @@ Apply this rule when enabling HTTP REST endpoints or WebSocket subscriptions for
50
51
  }
51
52
  ```
52
53
 
53
- Reference the schema file in `config.yaml`:
54
+ Without `@export`, the table has no REST route and callers receive `404`. Without `rest: true` in `config.yaml`, even an exported table does not respond to HTTP requests.
54
55
 
55
- ```yaml
56
- graphqlSchema:
57
- files: schema.graphql
58
- rest: true
59
- ```
56
+ 3. **Understand the auto-generated endpoints**. With both `@export` and `rest: true` in place, Harper registers the following on the application HTTP server port (default `9926`):
60
57
 
61
- 3. **Use the automatically registered endpoints**: Harper serves the following endpoints on the application HTTP server port (default `9926`) with no route definitions or handler code required.
58
+ | Endpoint | Description |
59
+ | ---------------------------- | ------------------------------------------------------------------------------------ |
60
+ | `GET /Product` | Resource description — table name, database, declared attributes |
61
+ | `GET /Product/` | Record collection; append query parameters to filter, sort, page |
62
+ | `GET /Product/{id}` | Single record by primary key; `404` if not found |
63
+ | `GET /Product/{id}.property` | Single declared property of one record |
64
+ | `POST /Product/` | Creates a record; responds `201`; primary key returned in `Location` header |
65
+ | `PUT /Product/{id}` | Creates or replaces the record at `{id}` (upsert); omitted properties are removed |
66
+ | `PATCH /Product/{id}` | Shallow-merges body into existing record; unspecified top-level properties preserved |
67
+ | `DELETE /Product/{id}` | Deletes the record at `{id}` |
68
+ | `DELETE /Product/?query` | Deletes every record matching the query |
69
+ - `HEAD` is served exactly as `GET` with the response body omitted. `QUERY` is accepted on the collection path (`QUERY /Product/`) and runs a search taken from the request body rather than the URL.
62
70
 
63
- | Endpoint | Description |
64
- | ---------------------------- | --------------------------------------------------------------------------- |
65
- | `GET /Product` | Returns resource description (table name, database, attributes) |
66
- | `GET /Product/` | Returns the record collection; append query parameters to filter |
67
- | `GET /Product/{id}` | Returns a single record by primary key; `404` if not found |
68
- | `GET /Product/{id}.property` | Returns a single declared property of one record |
69
- | `POST /Product/` | Creates a record; responds `201`; primary key returned in `Location` header |
70
- | `PUT /Product/{id}` | Creates or replaces the record at `{id}` (upsert) |
71
- | `PATCH /Product/{id}` | Merges body into existing record (shallow, top-level only) |
72
- | `DELETE /Product/{id}` | Deletes the record at `{id}` |
73
- | `DELETE /Product/?query` | Deletes every record matching the query |
71
+ 4. **Use the correct URL structure**. The trailing slash is significant:
74
72
 
75
- 4. **Handle `POST` primary key and `Location`**: On a successful `POST`, the new record's primary key is returned in the `Location` response header — the value the body supplied if it carried the primary-key property, otherwise a Harper-assigned key. The header carries the bare key value, not a URL.
73
+ | Path | Addresses |
74
+ | ------------------------- | ---------------------------------------------- |
75
+ | `/my-resource` | The resource itself (metadata) |
76
+ | `/my-resource/` | The record collection |
77
+ | `/my-resource/record-id` | A specific record by primary key |
78
+ | `/my-resource/record-id/` | Collection of records with the given id prefix |
76
79
 
77
- 5. **Understand `PUT` write behavior**: `PUT` replaces the stored record exactly. Three exceptions always apply: a `@createdTime` attribute keeps the original value, an `@updatedTime` attribute is re-stamped with the time of the write, and the primary key is forced to match the `{id}` in the URL.
80
+ 5. **Handle `POST` primary keys via `Location`**. On a successful `POST`, the new record's primary key is returned in the `Location` response header — the value supplied by the body, or a Harper-assigned key if the body omitted the primary-key property.
78
81
 
79
- 6. **Use conditional requests for caching**: GET responses include an `ETag` header encoding the record's version/last-modification time. Send `If-None-Match` on subsequent requests with the cached `ETag` value. If the record has not changed, Harper returns `304 Not Modified` with no body.
82
+ 6. **Use conditional GET requests for caching**. GET responses include an `ETag` header. Send `If-None-Match` on subsequent requests; if the record is unchanged, Harper returns `304 Not Modified` with no body.
80
83
 
81
- 7. **Select content type with `Accept`**: Use the `Accept` header to request a specific response format. The suffixes `.json`, `.cbor`, `.msgpack`, and `.csv` are reserved as content-type selectors on property paths and take precedence over property names. See [querying-rest-apis.md](querying-rest-apis.md) for query syntax details.
84
+ 7. **Apply `@updatedTime` for write timestamps**. On `PUT`, Harper re-stamps any `@updatedTime` attribute with the time of the write, preserves any `@createdTime` value from the original record, and forces the primary key to match the `{id}` in the URL.
82
85
 
83
- 8. **Connect via WebSocket**: WebSocket support is enabled automatically when `rest` is enabled. Connecting to a resource URL subscribes to changes for that resource. See [real-time-apps.md](real-time-apps.md) for real-time patterns.
84
-
85
- ```javascript
86
- let ws = new WebSocket('wss://server/my-resource/341');
87
- ws.onmessage = (event) => {
88
- let data = JSON.parse(event.data);
89
- };
90
- ```
86
+ 8. **Control OpenAPI visibility**. Every non-hidden exported resource appears in the generated OpenAPI document at `GET /openapi`. To exclude a type, mark it `@hidden` in the schema, or set `static hidden = true` on a programmatic resource class.
91
87
 
92
- 9. **Implement a custom `connect()` handler** when default subscription behavior is insufficient. The method must return an async iterable that produces messages to send to the client.
88
+ 9. **Implement a custom `connect()` handler** when default subscription behavior is insufficient. The method must return an async iterable that produces messages to send to the client:
93
89
 
94
90
  ```javascript
95
91
  export class Echo extends Resource {
@@ -101,9 +97,28 @@ Apply this rule when enabling HTTP REST endpoints or WebSocket subscriptions for
101
97
  }
102
98
  ```
103
99
 
100
+ 10. **Connect via WebSocket**. WebSocket support is enabled automatically with `rest: true`. Connect to a resource URL to subscribe to change events:
101
+
102
+ ```javascript
103
+ let ws = new WebSocket('wss://server/my-resource/341');
104
+ ws.onmessage = (event) => {
105
+ let data = JSON.parse(event.data);
106
+ };
107
+ ```
108
+
109
+ Disable WebSocket support independently with `webSocket: false` under `rest`.
110
+
111
+ 11. **Use MQTT over WebSockets** by setting the sub-protocol header:
112
+
113
+ ```
114
+ Sec-WebSocket-Protocol: mqtt
115
+ ```
116
+
117
+ 12. **Specify response format** using the `Accept` header. The suffixes `.json`, `.cbor`, `.msgpack`, and `.csv` are reserved as content-type selectors on property paths and take precedence over property names.
118
+
104
119
  ## Examples
105
120
 
106
- ### Full schema and config setup
121
+ **Schema and config for a REST-enabled table:**
107
122
 
108
123
  ```graphql
109
124
  # schema.graphql
@@ -121,89 +136,77 @@ graphqlSchema:
121
136
  rest: true
122
137
  ```
123
138
 
124
- ### Conditional GET with ETag caching
125
-
126
- ```
127
- GET /Product/123
128
- # Response includes:
129
- # ETag: "abc123"
139
+ **Conditional GET with ETag caching:**
130
140
 
141
+ ```http
131
142
  GET /Product/123
132
143
  If-None-Match: "abc123"
133
- # Response: 304 Not Modified (no body transferred)
134
144
  ```
135
145
 
136
- ### POST and read the Location header
146
+ Response when unchanged:
137
147
 
138
148
  ```
139
- POST /Product/
140
- Content-Type: application/json
141
-
142
- { "name": "Widget", "price": 9.99 }
143
-
144
- # Response:
145
- # 201 Created
146
- # Location: 7f3a9c
149
+ HTTP/1.1 304 Not Modified
150
+ ETag: "abc123"
147
151
  ```
148
152
 
149
- ### PATCH (shallow merge only)
153
+ **POST and read the Location header:**
150
154
 
151
- ```
152
- PATCH /Product/123
155
+ ```http
156
+ POST /Product/
153
157
  Content-Type: application/json
154
158
 
155
- { "price": 12.99 }
159
+ { "name": "Widget" }
156
160
  ```
157
161
 
158
- Only `price` is updated; other top-level properties are preserved. Nested objects in the body replace the stored sub-object wholesale — deep merge does not occur.
159
-
160
- ### Request MessagePack response
161
-
162
162
  ```
163
- GET /Product/123
164
- Accept: application/msgpack
163
+ HTTP/1.1 201 Created
164
+ Location: 7f3a9c
165
165
  ```
166
166
 
167
- Alternatively, use the `.msgpack` suffix on the URL path where supported.
167
+ **PATCH (shallow merge only):**
168
168
 
169
- ### WebSocket with custom outgoing messages
169
+ ```http
170
+ PATCH /Product/123
171
+ Content-Type: application/json
170
172
 
171
- ```javascript
172
- export class Example extends Resource {
173
- connect(incomingMessages) {
174
- let outgoingMessages = super.connect();
173
+ { "status": "active" }
174
+ ```
175
175
 
176
- let timer = setInterval(() => {
177
- outgoingMessages.send({ greeting: 'hi again!' });
178
- }, 1000);
176
+ **Custom WebSocket connect handler:**
179
177
 
180
- incomingMessages.on('data', (message) => {
181
- outgoingMessages.send(message);
182
- });
178
+ ```javascript
179
+ export class Echo extends Resource {
180
+ async *connect(incomingMessages) {
181
+ for await (let message of incomingMessages) {
182
+ yield message; // echo each message back
183
+ }
184
+ }
185
+ }
186
+ ```
183
187
 
184
- outgoingMessages.on('close', () => {
185
- clearInterval(timer);
186
- });
188
+ **Hide a resource from OpenAPI — schema directive:**
187
189
 
188
- return outgoingMessages;
189
- }
190
+ ```graphql
191
+ type InternalLog @table @export @hidden {
192
+ id: Long @primaryKey
190
193
  }
191
194
  ```
192
195
 
193
- ### Disable WebSocket while keeping REST
196
+ **Hide a resource from OpenAPI — programmatic class:**
194
197
 
195
- ```yaml
196
- rest:
197
- webSocket: false
198
+ ```javascript
199
+ export class InternalLog extends Resource {
200
+ static hidden = true;
201
+ }
198
202
  ```
199
203
 
200
204
  ## Notes
201
205
 
202
- - The trailing slash is significant: `/Product` addresses the resource itself; `/Product/` addresses its record collection. `POST /Product` (no trailing slash) returns `404`.
203
- - `HEAD` is served as `GET` with the body omitted. `QUERY` is accepted on the collection path and reads its search from the request body.
204
- - A `POST` to an existing primary key fails with `409` — it does not overwrite.
205
- - A component directory with **no configuration file** gets REST enabled by Harper's built-in default. As soon as a `config.yaml` exists it is used verbatim — add `rest: true` explicitly or REST is off.
206
- - Do not apply `@export` to a schema type and also export a same-named JavaScript subclass of that table — this produces conflicting endpoints.
207
- - Server-Sent Events subscriptions are served on the same paths, negotiated via `Accept: text/event-stream`. They are not affected by the `webSocket` option.
208
- - Every non-hidden exported resource is included in the generated OpenAPI document at `GET /openapi`. Mark a type `@hidden` or set `static hidden = true` on a programmatic Resource to omit it.
209
- - MQTT over WebSockets requires the sub-protocol header `Sec-WebSocket-Protocol: mqtt`.
206
+ - A component directory with **no** `config.yaml` inherits Harper's built-in default, which enables `rest` automatically. As soon as a `config.yaml` exists, it is used verbatim — omitting `rest` from it disables REST even if the directory previously had it without a config file. Always add `rest: true` when adding a config file.
207
+ - `POST /Product` (no trailing slash) returns `404`. The trailing slash is required for collection operations.
208
+ - A `POST` to a primary key that already exists fails with `409` rather than overwriting.
209
+ - PATCH merge is **shallow** — a nested object in the body replaces the stored nested object wholesale; nested properties not included in the body are dropped.
210
+ - The `.msgpack` suffix (along with `.json`, `.cbor`, `.csv`) is reserved as a content-type selector and cannot be used as a property name in dot-path access.
211
+ - Server-Sent Events subscriptions are served on the same paths, negotiated via `Accept: text/event-stream`; they are not affected by the `webSocket` option.
212
+ - See [querying-rest-apis.md](querying-rest-apis.md) for full query syntax on collection endpoints and [real-time-apps.md](real-time-apps.md) for real-time subscription patterns.
@@ -8,8 +8,8 @@ metadata:
8
8
  - >-
9
9
  fabric/cluster-creation-management.md#Connecting the Harper CLI to a
10
10
  Cluster
11
- sourceCommit: 677ad213d67822e109c83619e181ca23a59823db
12
- inputHash: e5586ad2bcc7c12a
11
+ sourceCommit: 2cfb81318f17e0aca2d600109e6ad813d2d0443e
12
+ inputHash: c821d15a19e2370f
13
13
  ---
14
14
 
15
15
  # Deploying to Harper Fabric
@@ -18,7 +18,7 @@ Instructions for the agent to follow when deploying a Harper application to a re
18
18
 
19
19
  ## When to Use
20
20
 
21
- Apply this rule when deploying a Harper application to a remote Harper Fabric cluster or any remote Harper instance. This includes first-time deploys, redeployments, rollbacks, CI/CD pipeline deploys, and provisioning credentials for private repositories. See [creating-a-fabric-account-and-cluster.md](creating-a-fabric-account-and-cluster.md) for setting up the cluster before deploying.
21
+ Apply this rule when deploying a Harper application to a remote Harper instance or Fabric cluster, including first-time deploys, redeployments, rollbacks, and CI/CD pipeline deployments. Also apply it when provisioning credentials for private repository deploys. See [creating-a-fabric-account-and-cluster.md](creating-a-fabric-account-and-cluster.md) for setting up the cluster before deploying.
22
22
 
23
23
  ## How It Works
24
24
 
@@ -29,7 +29,7 @@ Apply this rule when deploying a Harper application to a remote Harper Fabric cl
29
29
  # Provide cluster username and password when prompted
30
30
  ```
31
31
 
32
- 2. **Deploy the application**: After login, run `harper deploy` without repeating credentials. Set `restart=true` and `replicated=true` for a production deploy.
32
+ 2. **Deploy the application**: After login, run `harper deploy` without repeating credentials. Use `restart=true` and `replicated=true` for production deploys.
33
33
 
34
34
  ```bash
35
35
  harper deploy \
@@ -40,42 +40,32 @@ Apply this rule when deploying a Harper application to a remote Harper Fabric cl
40
40
  replicated=true
41
41
  ```
42
42
 
43
- 3. **Use environment variables for CI/CD**: Instead of `harper login`, export credentials as environment variables before calling `harper deploy`.
43
+ 3. **Choose a package source**: Set the `package` parameter to any valid npm dependency value. Options:
44
44
 
45
- ```bash
46
- export HARPER_CLI_USERNAME=<username>
47
- export HARPER_CLI_PASSWORD=<password>
48
- harper deploy \
49
- project=<name> \
50
- package=<package> \
51
- target=<remote> \
52
- restart=true \
53
- replicated=true
54
- ```
55
-
56
- 4. **Choose a package source**: The `package` field accepts any valid npm dependency value. Select the form that matches your source:
57
-
58
- | Source | `package` value |
59
- | ----------------------- | ---------------------------------------------------- |
60
- | Current local directory | Omit `package` |
61
- | npm package | `"@harperdb/status-check"` |
62
- | GitHub (public) | `"HarperFast/status-check"` or full URL |
63
- | Private repo (SSH) | `"git+ssh://git@github.com:HarperDB/secret-app.git"` |
64
- | Tarball | `"https://example.com/application.tar.gz"` |
45
+ | Source | Example value |
46
+ | ----------------------- | ------------------------------------------------------------ |
47
+ | Current local directory | Omit `package` |
48
+ | npm package | `package="@harperdb/status-check"` |
49
+ | GitHub (shorthand) | `package="HarperFast/status-check"` |
50
+ | GitHub (URL) | `package="https://github.com/HarperFast/status-check"` |
51
+ | Private repo (SSH) | `package="git+ssh://git@github.com:HarperDB/secret-app.git"` |
52
+ | Tarball | `package="https://example.com/application.tar.gz"` |
65
53
 
66
- For git tags, use the `semver` directive:
54
+ When using git tags, use the `semver` directive:
67
55
 
68
56
  ```
69
57
  HarperFast/application-template#semver:v1.0.0
70
58
  ```
71
59
 
72
- 5. **Deploy by reference for reproducible deploys**: Pass `by_ref=true` to send a pinned git SHA instead of uploading a snapshot. The cluster fetches and builds from that exact commit.
60
+ 4. **Deploy by reference (pinned commit)**: Use `by_ref=true` to send a pinned git reference instead of uploading a snapshot. The cluster fetches and builds from that exact commit SHA.
73
61
 
74
62
  ```bash
75
63
  harper deploy by_ref=true restart=true replicated=true
76
64
  ```
77
65
 
78
- Use `ref` to target a specific commit, tag, or branch (resolved to a full SHA before sending):
66
+ - `by_ref` — Build the package reference from the local repository.
67
+ - `ref` _(optional)_ — Deploy a specific commit, tag, or branch instead of `HEAD`. Implies `by_ref`. Tags and branches in `refs/tags` and `refs/heads` namespaces are resolved to a full commit SHA before the deploy is sent.
68
+ - `credential` _(optional)_ — Set to `true` to authenticate the clone with the stored credential for the repository's host. Omit for public repositories.
79
69
 
80
70
  ```bash
81
71
  # Deploy a specific tag
@@ -85,32 +75,61 @@ Apply this rule when deploying a Harper application to a remote Harper Fabric cl
85
75
  harper deploy ref=9f8c2a1 restart=true replicated=true
86
76
  ```
87
77
 
88
- **Key constraints for `ref` values:**
89
- - Must name something a clone can fetch: `refs/heads/*` and `refs/tags/*`, or a bare branch or tag name.
90
- - Anything else (e.g., `refs/pull/123/head`) is rejected up front.
91
- - If a ref can't be resolved, the deploy stops — run `git fetch` and retry, or pass a full commit SHA.
92
- - Commit and push before deploying: the cluster clones from the remote and only sees pushed commits.
78
+ **Important constraints on refs:**
79
+ - A full commit SHA is accepted directly with no resolution.
80
+ - Every other `ref` must resolve to something a clone can fetch: `refs/tags/*` or `refs/heads/*`, or a bare branch or tag name.
81
+ - Qualified refs outside those two namespaces (e.g., `refs/pull/123/head`) are rejected.
82
+ - Commit and push before deploying — the cluster clones from the remote and only sees pushed commits.
83
+ - Run `git fetch` if a ref can't be resolved, or pass a full commit SHA.
93
84
 
94
- 6. **Deploy private repositories by reference**: Pass `credential=true` alongside `by_ref=true`. The CLI attaches a credentials reference; the cluster resolves the secret in memory at clone time — no token travels in the operation body or lands on disk.
85
+ 5. **Handle private repositories**: Pass `credential=true` so the CLI attaches a credentials reference that the cluster resolves in memory at clone time. No token travels in the operation body or lands on disk.
95
86
 
96
87
  ```bash
97
88
  harper deploy by_ref=true credential=true restart=true replicated=true
98
89
  ```
99
90
 
100
- 7. **Provision a deploy credential for private sources**: Run `harper deploy setup=true` once per component and source. This is interactive and requires **super_user** — run it with an administrative credential, not the CI identity.
91
+ Provision the credential once with `setup=true` before using `credential=true`.
92
+
93
+ 6. **Provision a deploy credential**: Run `harper deploy setup=true` once per component and source to provision credentials for private deploys. This operation requires **super_user** — run it with an administrative credential, not the CI identity.
101
94
 
102
95
  ```bash
103
96
  harper deploy setup=true
104
97
  ```
105
98
 
106
- This command:
99
+ This interactive command:
107
100
  1. Fetches the cluster's public key with `get_secrets_public_key`.
108
101
  2. Encrypts the token locally into an `enc:v1:` envelope.
109
102
  3. Stores only the ciphertext with `set_secret`, in the component-scoped tier.
110
103
  4. Grants the component permission to resolve it with `grant_secret`.
111
104
  5. Prints the `credentials` reference for the deploy to use.
112
105
 
113
- Use a **fine-grained** personal access token (PAT) scoped to **Contents: Read-only** on the specific repository. Avoid session tokens from `gh` CLI — they typically carry `repo`, `read:org`, `gist`, and `workflow` scopes across your whole account.
106
+ **Use a fine-grained PAT** for GitHub repositories. The prompt defaults to a fine-grained personal access token with **Contents: Read-only** on that one repository. If you use the `gh` CLI session token instead, it typically carries `read:org`, `repo`, `gist`, and `workflow` scopes across your whole account — the CLI prints a warning if you choose it. Use the narrowest credential that does the job, because the stored token is replayed on every cold deploy and rollback.
107
+
108
+ 7. **Use environment variables for CI/CD**: Instead of `harper login`, export credentials as environment variables.
109
+
110
+ ```bash
111
+ export HARPER_CLI_USERNAME=<username>
112
+ export HARPER_CLI_PASSWORD=<password>
113
+ harper deploy \
114
+ project=<name> \
115
+ package=<package> \
116
+ target=<remote> \
117
+ restart=true \
118
+ replicated=true
119
+ ```
120
+
121
+ 8. **Use dedicated auth parameters for one-off commands** (not recommended for production): Pass `auth_username` and `auth_password` directly. These take precedence over environment variables and saved login tokens.
122
+
123
+ ```bash
124
+ harper deploy \
125
+ project=<name> \
126
+ package=<package> \
127
+ auth_username=<username> \
128
+ auth_password=<password> \
129
+ target=<remote> \
130
+ restart=true \
131
+ replicated=true
132
+ ```
114
133
 
115
134
  ## Examples
116
135
 
@@ -126,41 +145,48 @@ harper deploy \
126
145
  replicated=true
127
146
  ```
128
147
 
129
- **CI/CD deploy using environment variables:**
148
+ **Deploy by reference with a tag:**
130
149
 
131
150
  ```bash
132
- export HARPER_CLI_USERNAME=admin
133
- export HARPER_CLI_PASSWORD=secret
134
- harper deploy \
135
- project=my-app \
136
- package="HarperFast/my-app" \
137
- target=https://my-cluster.harperdbcloud.com \
138
- restart=true \
139
- replicated=true
151
+ harper deploy ref=v1.2.0 restart=true replicated=true
140
152
  ```
141
153
 
142
- **Deploy by reference in GitHub Actions (pull request):**
154
+ **Deploy a private GitHub repo by reference:**
143
155
 
144
156
  ```bash
145
- harper deploy ref=${{ github.event.pull_request.head.sha }} restart=true replicated=true
157
+ # Provision credential once (requires super_user)
158
+ harper deploy setup=true
159
+
160
+ # Deploy using stored credential
161
+ harper deploy by_ref=true credential=true restart=true replicated=true
146
162
  ```
147
163
 
148
- **Deploy a private repo by reference with a provisioned credential:**
164
+ **GitHub Actions — pull request deploy:**
149
165
 
150
166
  ```bash
151
- # Provision once (run as super_user)
152
- harper deploy setup=true
167
+ harper deploy ref=${{ github.event.pull_request.head.sha }} restart=true replicated=true
168
+ ```
153
169
 
154
- # Deploy subsequently
155
- harper deploy by_ref=true credential=true restart=true replicated=true
170
+ **CI/CD deploy using environment variables:**
171
+
172
+ ```bash
173
+ export HARPER_CLI_USERNAME=<username>
174
+ export HARPER_CLI_PASSWORD=<password>
175
+ harper deploy \
176
+ project=my-app \
177
+ package="@myorg/my-app" \
178
+ target=https://my-cluster.harperdbcloud.com \
179
+ restart=true \
180
+ replicated=true
156
181
  ```
157
182
 
158
183
  ## Notes
159
184
 
160
- - `auth_username` and `auth_password` can be passed directly as deploy parameters for one-off commands, but this is not recommended for production. Dedicated authentication parameters take precedence over environment variables and saved login tokens.
185
+ - The cluster's Application URL is found on the **Config → Overview** page of the Fabric dashboard.
186
+ - `harper deploy setup=true` calls `get_secrets_public_key`, `set_secret`, and `grant_secret`, all of which require **super_user**. Do not run it with the CI identity.
161
187
  - The `enc:v1:` envelope means the plaintext token never leaves your machine — only ciphertext is stored and replicated.
162
- - Deploy credentials are stored scoped to the component, never in the global `processEnv` tier. If a global secret exists at the derived name, it is converted to the component-scoped tier automatically.
163
- - Because stored credentials are durable, later deploys and rollbacks reuse them without re-entering anything.
164
- - The unpushed-commit check is skipped under GitHub Actions; the dirty-tree warning still applies.
165
- - Deploying by reference means the cluster installs and builds from source. If your application requires a build step that cannot run on the node, deploy the built output as a payload deploy instead.
188
+ - Secrets are stored scoped to the component, not in the global `processEnv` tier. If a global secret already exists at the derived name, it is converted to the scoped tier.
166
189
  - For SSH-based private repos, use the `add_ssh_key` operation to register keys before deploying.
190
+ - If your application requires a build step that cannot run on the cluster node, deploy a built payload (omit `by_ref`) instead of deploying by reference.
191
+ - The unpushed-commit check is skipped under GitHub Actions; the dirty-tree warning still applies.
192
+ - Annotated tags in `refs/tags` resolve to the commit they point at, not the tag object.