@mortar-ai/skill 0.2.0 → 0.5.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 +33 -25
- package/SKILL.md +90 -147
- package/bin/install.js +8 -3
- package/index.js +2 -0
- package/manifest.json +4 -4
- package/package.json +5 -5
- package/references/analytics.md +29 -0
- package/references/architecture.md +99 -0
- package/references/auth.md +33 -0
- package/references/automation.md +23 -0
- package/references/communications.md +16 -0
- package/references/control-plane.md +25 -0
- package/references/database.md +101 -0
- package/references/functions.md +38 -0
- package/references/realtime.md +33 -0
- package/references/secrets.md +17 -0
- package/references/storage.md +48 -0
- package/references/usage-billing.md +27 -0
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# @mortar-ai/skill
|
|
2
2
|
|
|
3
3
|
[Agent Skills](https://agentskills.io/) package for building on **Mortar** (the
|
|
4
|
-
BaaS that AI bundles talk to). Install the canonical `SKILL.md`
|
|
5
|
-
agent's skill directory to teach it the SDK
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
BaaS that AI bundles talk to). Install the canonical `SKILL.md` and its
|
|
5
|
+
`references/` into a compatible agent's skill directory to teach it the SDK
|
|
6
|
+
core invariants plus on-demand capability references. The split keeps Fabric's
|
|
7
|
+
loaded prompt small without hiding the storage, realtime, functions, auth, or
|
|
8
|
+
database guidance when a task needs it.
|
|
9
9
|
|
|
10
10
|
## Install
|
|
11
11
|
|
|
@@ -17,8 +17,9 @@ mortar-skill-install
|
|
|
17
17
|
|
|
18
18
|
Then restart Claude Code (or `/skill reload`).
|
|
19
19
|
|
|
20
|
-
The same versioned `SKILL.md` is
|
|
21
|
-
binds the npm version and Harness preinstall to
|
|
20
|
+
The same versioned `SKILL.md` and `references/` package is built into Fabric
|
|
21
|
+
Harness. `manifest.json` binds the npm version and Harness preinstall to a
|
|
22
|
+
deterministic package SHA-256 digest; the Fabric
|
|
22
23
|
Server rejects a Market install when the running Harness reports different
|
|
23
24
|
bytes. npm is an external distribution channel and is not required by Fabric at
|
|
24
25
|
runtime.
|
|
@@ -32,22 +33,26 @@ experience.
|
|
|
32
33
|
|
|
33
34
|
The skill teaches Claude:
|
|
34
35
|
|
|
35
|
-
- **
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
- **
|
|
39
|
-
|
|
40
|
-
- **
|
|
41
|
-
|
|
42
|
-
- **
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
36
|
+
- **Core contract** — Fabric-owned client/config files, schema-first workflow,
|
|
37
|
+
one migration operation per call, supported migration kinds, access-policy
|
|
38
|
+
invariants, version boundaries, and completion checks.
|
|
39
|
+
- **Database reference** — CRUD, filters, atomic row mutations, trusted
|
|
40
|
+
cross-table transactions, and ACID boundaries.
|
|
41
|
+
- **Auth reference** — email/password, phone OTP, sessions, password reset, and
|
|
42
|
+
public-access policy choices.
|
|
43
|
+
- **Storage reference** — upload/download, signed URLs, immutable physical
|
|
44
|
+
versions, metadata pointers, CORS, and row/object consistency boundaries.
|
|
45
|
+
- **Realtime reference** — Postgres changes, broadcast, presence, unsubscribe,
|
|
46
|
+
and teardown.
|
|
47
|
+
- **Functions + automation** — invoke/deploy boundaries, cron, queues, and
|
|
48
|
+
transactional enqueue.
|
|
49
|
+
- **Analytics + operations** — capture/flush, usage, managed logs, primitive
|
|
50
|
+
billing attribution, and object-egress evidence.
|
|
51
|
+
- **Trusted services** — communications, encrypted secrets, accounts/projects,
|
|
52
|
+
keys/PATs, custom domains, top-up, and capacity.
|
|
53
|
+
- **Architecture reference** — all 9 infrastructure primitives, the open
|
|
54
|
+
capability registry, application modules, usage accounting, and
|
|
55
|
+
credential/trust surfaces.
|
|
51
56
|
|
|
52
57
|
## Custom install location
|
|
53
58
|
|
|
@@ -64,8 +69,9 @@ Idempotent — overwrites in place.
|
|
|
64
69
|
|
|
65
70
|
## Maintainer release
|
|
66
71
|
|
|
67
|
-
`SKILL.md`
|
|
68
|
-
and
|
|
72
|
+
`SKILL.md` and `references/` are the hand-edited instruction sources. Keep the
|
|
73
|
+
core short and move capability detail into a routed reference. Regenerate the
|
|
74
|
+
manifest and Harness copy before testing or publishing:
|
|
69
75
|
|
|
70
76
|
```bash
|
|
71
77
|
npm run sync
|
|
@@ -73,6 +79,8 @@ npm run check
|
|
|
73
79
|
npm pack --dry-run
|
|
74
80
|
```
|
|
75
81
|
|
|
82
|
+
The repository-level synchronizer is shared with Keel and fails on missing
|
|
83
|
+
configured reference directories, resource drift, or unexpected Harness files.
|
|
76
84
|
Never edit `fabric/harness/internal/skills/preinstalled/mortar/` directly.
|
|
77
85
|
|
|
78
86
|
## License
|
package/SKILL.md
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
3
|
-
description:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
name: mortar
|
|
3
|
+
description: Build or change Fabric applications that use Mortar capabilities such as database, auth, storage, realtime, functions, analytics, automation, or messaging. Use for Mortar schema and provisioning, SDK integration, access policy, transactions, or backend consistency; not for Fabric's own control plane or a different backend.
|
|
4
|
+
metadata:
|
|
5
|
+
activation: auto
|
|
6
|
+
priority: high
|
|
7
|
+
triggers:
|
|
8
|
+
- keywords: [database, 数据库, backend, 后端, auth, authentication, 登录, 认证, persistence, 持久化, transaction, 事务, ACID, storage, upload, 文件, realtime, 实时, collaboration, function, compute, analytics, cron, queue, comms, secrets, billing, cloud, 云端]
|
|
8
9
|
---
|
|
9
10
|
|
|
10
11
|
# Mortar for Fabric applications
|
|
@@ -58,152 +59,94 @@ below; if platform provisioning fails, report that platform failure.
|
|
|
58
59
|
SQL there: a file in that directory does not execute or change the live
|
|
59
60
|
tenant. Do not edit or create `mortar.config.json`. Do not edit generated
|
|
60
61
|
`integrations/mortar/types.ts`; Fabric regenerates it from the live schema.
|
|
61
|
-
4.
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
4. Install or update `@mortar-ai/client` with npm when needed and let npm own the
|
|
63
|
+
lockfile. Atomic batches, server-side filtered mutations, and
|
|
64
|
+
`DatabaseTransactions` require version 0.8.0 or newer. Do not rewrite
|
|
65
|
+
unrelated dependencies.
|
|
66
|
+
5. Before a multi-step mutation, choose the real consistency boundary. A
|
|
67
|
+
one-table batch or filtered mutation may use the public singleton;
|
|
68
|
+
cross-table row + queue transactions require trusted server code; object
|
|
69
|
+
storage and external services never join a PostgreSQL transaction.
|
|
70
|
+
6. Type-check and build. Handle every `{ data, error }` SDK result in UI code.
|
|
64
71
|
|
|
65
72
|
Supported migration kinds are `create_table`, `drop_table`, `set_table_access`,
|
|
66
|
-
`add_column`, `drop_column`, `create_index`, `set_bucket_access`,
|
|
67
|
-
`upsert_cron`, and `delete_cron`.
|
|
68
|
-
must respect the workspace approval
|
|
69
|
-
PostgreSQL, data backfills, PL/pgSQL
|
|
70
|
-
not migration inputs. If required
|
|
71
|
-
|
|
72
|
-
file that only looks executable.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
`
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
.
|
|
103
|
-
|
|
104
|
-
.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
Use `onAuthStateChange` when UI must react to session changes, and unsubscribe
|
|
131
|
-
the returned subscription during cleanup.
|
|
132
|
-
|
|
133
|
-
Password reset starts with `mortar.auth.resetPasswordForEmail(email)`; confirm via
|
|
134
|
-
`passwordResetConfirm(email, token, newPassword)`. Never store or pass session
|
|
135
|
-
tokens manually unless the user explicitly needs a server integration.
|
|
136
|
-
|
|
137
|
-
For private user rows, include the authenticated user's ID in the schema/data
|
|
138
|
-
model and choose the correct table access policy. Client-side filters improve
|
|
139
|
-
UX but do not replace server-side access rules.
|
|
140
|
-
|
|
141
|
-
## File storage
|
|
142
|
-
|
|
143
|
-
```ts
|
|
144
|
-
const bucket = mortar.storage.from('avatars');
|
|
145
|
-
|
|
146
|
-
const { data: uploaded, error: uploadError } = await bucket.upload(path, file, {
|
|
147
|
-
contentType: file.type,
|
|
148
|
-
upsert: true,
|
|
149
|
-
});
|
|
150
|
-
const { data: blob, error: downloadError } = await bucket.download(path);
|
|
151
|
-
const { data: files, error: listError } = await bucket.list('users/');
|
|
152
|
-
const { data: signed, error: signError } = await bucket.createSignedUrl(path, 3600);
|
|
153
|
-
const { error: removeError } = await bucket.remove([path]);
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
Downloads mint a short-lived URL and fetch the object directly from the storage
|
|
157
|
-
backend. Reuse `signed.signedUrl` until it nears expiry when rendering media.
|
|
158
|
-
Prefer the signed URL directly in `<img src>`, `<video src>`, or `<audio src>`;
|
|
159
|
-
this avoids an unnecessary blob copy and does not require CORS merely to render
|
|
160
|
-
the media. Signed-URL minting is excluded from the project's API-concurrency
|
|
161
|
-
budget, but clients should still lazy-load visible media and cache/reuse each
|
|
162
|
-
URL instead of signing again on every render. Signing authorizes a possible
|
|
163
|
-
download; it does not prove bytes were served and must never be described or
|
|
164
|
-
modeled as an object-egress charge. Only provider-observed access-log or billing
|
|
165
|
-
reconciliation may record actual object egress. The optional third
|
|
166
|
-
`createSignedUrl` argument (`{ signal, timeoutMs }`) bounds only the signing
|
|
167
|
-
request when the UI needs a deadline.
|
|
168
|
-
|
|
169
|
-
`bucket.download(path)` uses JavaScript `fetch()` against the object store and
|
|
170
|
-
therefore needs a matching bucket CORS rule in browsers. Appunvs-managed Mortar
|
|
171
|
-
storage provides that contract; self-hosted object storage must allow the app
|
|
172
|
-
origin for credential-free `GET` / `HEAD`. Never attach Mortar Authorization or
|
|
173
|
-
browser cookies to a signed object URL.
|
|
174
|
-
|
|
175
|
-
## Realtime, broadcast, and presence
|
|
176
|
-
|
|
177
|
-
```ts
|
|
178
|
-
const room = mortar
|
|
179
|
-
.channel('todos-room', { presence: { key: userId } })
|
|
180
|
-
.on(
|
|
181
|
-
'postgres_changes',
|
|
182
|
-
{ event: '*', schema: 'public', table: 'todos' },
|
|
183
|
-
(payload) => refreshFromChange(payload),
|
|
184
|
-
)
|
|
185
|
-
.on('broadcast', { event: 'cursor' }, ({ payload }) => drawCursor(payload))
|
|
186
|
-
.on('presence', { event: 'sync' }, () => renderRoster(room.presenceState()))
|
|
187
|
-
.subscribe((status) => {
|
|
188
|
-
if (status === 'SUBSCRIBED') void room.track({ online: true });
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
await room.send({ type: 'broadcast', event: 'cursor', payload: { x, y } });
|
|
192
|
-
// On component/page cleanup:
|
|
193
|
-
mortar.removeChannel(room);
|
|
73
|
+
`add_column`, `drop_column`, `create_index`, `set_bucket_access`,
|
|
74
|
+
`deploy_function`, `delete_function`, `upsert_cron`, and `delete_cron`.
|
|
75
|
+
Destructive and backend-changing operations must respect the workspace approval
|
|
76
|
+
gate. This list is exhaustive: arbitrary PostgreSQL, data backfills, PL/pgSQL
|
|
77
|
+
functions, transactions, and triggers are not migration inputs. If required
|
|
78
|
+
behavior cannot be expressed by one of these operations, report the unsupported
|
|
79
|
+
Mortar capability instead of authoring a SQL file that only looks executable.
|
|
80
|
+
|
|
81
|
+
## Access and safety invariants
|
|
82
|
+
|
|
83
|
+
- Change an existing table's access with `set_table_access`; never drop and
|
|
84
|
+
recreate a table merely to change permissions.
|
|
85
|
+
- For a public form, registration, or evidence upload reviewed by an operator,
|
|
86
|
+
use table/bucket access `public_insert`. It permits new submissions while
|
|
87
|
+
denying anonymous list, read, update, delete, and overwrite.
|
|
88
|
+
- Never use `public` merely to make anonymous submission work: it exposes all
|
|
89
|
+
rows or files for public read and mutation.
|
|
90
|
+
- Generate an unguessable logical object key for each public upload.
|
|
91
|
+
- Client-side filters are UX, not authorization. Choose a server-enforced access
|
|
92
|
+
policy for private or owner-scoped rows.
|
|
93
|
+
- Never put `app` or `admin` keys, `DatabaseTransactions`, operator clients, or
|
|
94
|
+
other privileged capabilities in Web, Desktop renderer, Keel/React Native, or
|
|
95
|
+
WeChat bundles.
|
|
96
|
+
- Never call a database + storage/functions/external workflow "atomic". Only
|
|
97
|
+
work committed inside one tenant-scoped PostgreSQL transaction is ACID.
|
|
98
|
+
|
|
99
|
+
## On-demand references
|
|
100
|
+
|
|
101
|
+
The following detailed references are bundled beside this core Skill. Read only
|
|
102
|
+
the documents needed for the current task; do not load all of them by default.
|
|
103
|
+
In Fabric they are materialized under `/tmp/skills/mortar/references/`; other
|
|
104
|
+
Agent Skills hosts resolve the same paths relative to this `SKILL.md`.
|
|
105
|
+
|
|
106
|
+
- Database CRUD, atomic row mutations, trusted cross-table transactions, and
|
|
107
|
+
exact ACID boundaries: `references/database.md`
|
|
108
|
+
- App-user signup/signin, OTP, session lifecycle, and private-user modeling:
|
|
109
|
+
`references/auth.md`
|
|
110
|
+
- Upload/download/list/signed URLs, immutable object versions, CORS, and
|
|
111
|
+
database-to-object consistency: `references/storage.md`
|
|
112
|
+
- Postgres changes, broadcast, presence, reconnect behavior, and cleanup:
|
|
113
|
+
`references/realtime.md`
|
|
114
|
+
- Function deployment/invocation, logs, public-key boundaries, and error
|
|
115
|
+
handling: `references/functions.md`
|
|
116
|
+
- The 9 infrastructure primitives, open capability registry, application
|
|
117
|
+
modules, usage accounting, and trust surfaces: `references/architecture.md`
|
|
118
|
+
- Product analytics, error capture, delivery guarantees, and operator summary:
|
|
119
|
+
`references/analytics.md`
|
|
120
|
+
- Cron schedules, queues, workers, retries, and transactional enqueue:
|
|
121
|
+
`references/automation.md`
|
|
122
|
+
- Transactional email/SMS and provider/idempotency boundaries:
|
|
123
|
+
`references/communications.md`
|
|
124
|
+
- Encrypted project secrets and function environment injection:
|
|
125
|
+
`references/secrets.md`
|
|
126
|
+
- Project usage, managed logs, billing attribution, and egress semantics:
|
|
127
|
+
`references/usage-billing.md`
|
|
128
|
+
- Accounts, projects, API keys, PATs, custom domains, top-up, and capacity:
|
|
129
|
+
`references/control-plane.md`
|
|
130
|
+
|
|
131
|
+
When using Fabric `bash`, read the exact materialized path shown by `load_skill`,
|
|
132
|
+
for example:
|
|
133
|
+
|
|
134
|
+
```sh
|
|
135
|
+
sed -n '1,240p' /tmp/skills/mortar/references/storage.md
|
|
194
136
|
```
|
|
195
137
|
|
|
196
|
-
|
|
197
|
-
realtime only notifies peers.
|
|
198
|
-
|
|
199
|
-
## Compute and analytics
|
|
138
|
+
## Completion checklist
|
|
200
139
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
140
|
+
- Live schema was read before schema design.
|
|
141
|
+
- Every live schema change went through one approved `mortar_migrate` operation.
|
|
142
|
+
- Application code imports only the platform-owned singleton.
|
|
143
|
+
- No secret/admin client or hand-written fallback configuration entered the
|
|
144
|
+
client bundle.
|
|
145
|
+
- Every SDK error path is handled.
|
|
146
|
+
- The relevant on-demand reference—not every reference—was consulted.
|
|
147
|
+
- Type-check and the surface's real build pass.
|
|
205
148
|
|
|
206
149
|
## Boundary for non-Fabric development
|
|
207
150
|
|
|
208
|
-
For Mortar itself or a standalone non-Fabric integration, inspect the current
|
|
209
|
-
README. Never copy its standalone `createClient` setup into a Fabric project.
|
|
151
|
+
For Mortar itself or a standalone non-Fabric integration, inspect the current
|
|
152
|
+
SDK README. Never copy its standalone `createClient` setup into a Fabric project.
|
package/bin/install.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* mortar-skill-install — copies the canonical SKILL.md
|
|
4
|
-
* Code skills directory.
|
|
3
|
+
* mortar-skill-install — copies the canonical SKILL.md and its on-demand
|
|
4
|
+
* references into the user's Claude Code skills directory.
|
|
5
5
|
*
|
|
6
6
|
* Default destination: `~/.claude/skills/mortar/SKILL.md`. Override
|
|
7
7
|
* via `--dest=<path>` for non-default Claude installs.
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
'use strict';
|
|
12
12
|
|
|
13
|
-
const { copyFileSync, existsSync, mkdirSync } = require('node:fs');
|
|
13
|
+
const { copyFileSync, cpSync, existsSync, mkdirSync, rmSync } = require('node:fs');
|
|
14
14
|
const { homedir } = require('node:os');
|
|
15
15
|
const { dirname, resolve } = require('node:path');
|
|
16
16
|
|
|
@@ -27,6 +27,7 @@ function parseFlags(argv) {
|
|
|
27
27
|
function main() {
|
|
28
28
|
const flags = parseFlags(process.argv);
|
|
29
29
|
const src = resolve(__dirname, '..', 'SKILL.md');
|
|
30
|
+
const referencesSrc = resolve(__dirname, '..', 'references');
|
|
30
31
|
if (!existsSync(src)) {
|
|
31
32
|
console.error(`mortar-skill: source missing at ${src}`);
|
|
32
33
|
process.exit(1);
|
|
@@ -37,7 +38,11 @@ function main() {
|
|
|
37
38
|
|
|
38
39
|
mkdirSync(dirname(dest), { recursive: true });
|
|
39
40
|
copyFileSync(src, dest);
|
|
41
|
+
const referencesDest = resolve(dirname(dest), 'references');
|
|
42
|
+
rmSync(referencesDest, { recursive: true, force: true });
|
|
43
|
+
if (existsSync(referencesSrc)) cpSync(referencesSrc, referencesDest, { recursive: true });
|
|
40
44
|
console.log(`✓ Installed Mortar skill → ${dest}`);
|
|
45
|
+
console.log(`✓ Installed on-demand references → ${referencesDest}`);
|
|
41
46
|
console.log('');
|
|
42
47
|
console.log('Restart Claude Code (or run `/skill reload`) to activate.');
|
|
43
48
|
}
|
package/index.js
CHANGED
|
@@ -10,6 +10,7 @@ const { readFileSync } = require('node:fs');
|
|
|
10
10
|
const { resolve } = require('node:path');
|
|
11
11
|
|
|
12
12
|
const SKILL_PATH = resolve(__dirname, 'SKILL.md');
|
|
13
|
+
const REFERENCES_PATH = resolve(__dirname, 'references');
|
|
13
14
|
const MANIFEST_PATH = resolve(__dirname, 'manifest.json');
|
|
14
15
|
|
|
15
16
|
module.exports = {
|
|
@@ -25,5 +26,6 @@ module.exports = {
|
|
|
25
26
|
return JSON.parse(readFileSync(MANIFEST_PATH, 'utf8'));
|
|
26
27
|
},
|
|
27
28
|
SKILL_PATH,
|
|
29
|
+
REFERENCES_PATH,
|
|
28
30
|
MANIFEST_PATH,
|
|
29
31
|
};
|
package/manifest.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": 1,
|
|
3
3
|
"id": "mortar",
|
|
4
|
-
"version": "0.
|
|
5
|
-
"content_sha256": "sha256:
|
|
4
|
+
"version": "0.5.0",
|
|
5
|
+
"content_sha256": "sha256:cbfe4219fa15934c523c1c22fe9795489539d0083d199624d2d15ac50bfe7755",
|
|
6
6
|
"source_repo": "liamxujia/appunvs",
|
|
7
|
-
"source_path": "mortar/skill
|
|
7
|
+
"source_path": "mortar/skill",
|
|
8
8
|
"distributions": [
|
|
9
9
|
"harness_preinstalled",
|
|
10
10
|
"npm"
|
|
11
11
|
],
|
|
12
12
|
"npm": {
|
|
13
13
|
"package": "@mortar-ai/skill",
|
|
14
|
-
"version": "0.
|
|
14
|
+
"version": "0.5.0"
|
|
15
15
|
}
|
|
16
16
|
}
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mortar-ai/skill",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Agent skill package for building Fabric applications on Mortar (BaaS),
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Agent skill package for building Fabric applications on Mortar (BaaS), with compact core guidance and on-demand references.",
|
|
5
5
|
"main": "index.js",
|
|
6
|
-
"files": ["SKILL.md", "manifest.json", "index.js", "README.md"],
|
|
6
|
+
"files": ["SKILL.md", "references", "manifest.json", "index.js", "README.md"],
|
|
7
7
|
"bin": {
|
|
8
8
|
"mortar-skill-install": "bin/install.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
-
"sync": "node
|
|
12
|
-
"check": "node scripts/
|
|
11
|
+
"sync": "node ../../shared/skill-distribution/sync.js --config distribution.json",
|
|
12
|
+
"check": "node scripts/check-coverage.js && node ../../shared/skill-distribution/sync.js --config distribution.json --check"
|
|
13
13
|
},
|
|
14
14
|
"publishConfig": { "access": "public" },
|
|
15
15
|
"license": "Apache-2.0"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Analytics and error-capture reference
|
|
2
|
+
|
|
3
|
+
Read this reference for product events, identity traits, error capture, flush
|
|
4
|
+
behavior, or operator summaries. Analytics composes the `warehouse`, `network`,
|
|
5
|
+
and operational logging paths; it is a capability, not a new cost primitive.
|
|
6
|
+
|
|
7
|
+
The public client batches events and exposes synchronous capture calls plus an
|
|
8
|
+
async flush:
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
mortar.analytics.track('pageview', { path: location.pathname });
|
|
12
|
+
mortar.analytics.identify(user.id, { plan: 'pro' });
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
await checkout();
|
|
16
|
+
} catch (error) {
|
|
17
|
+
mortar.analytics.captureError(error, { where: 'checkout' });
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
await mortar.analytics.flush();
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Do not treat capture or flush as a durable business transaction and do not block
|
|
25
|
+
primary UX on analytics delivery. Flush at an explicit lifecycle boundary when
|
|
26
|
+
best-effort delivery should be attempted before navigation or shutdown. The
|
|
27
|
+
summary endpoint is a trusted project/operator read, not part of Fabric's public
|
|
28
|
+
bundle singleton. A deployment without a configured warehouse returns an
|
|
29
|
+
availability error; do not silently invent local analytics persistence.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Architecture and capability taxonomy reference
|
|
2
|
+
|
|
3
|
+
Read this reference when deciding where a Mortar API belongs, which credential
|
|
4
|
+
may call it, which code implements it, which infrastructure it consumes, or
|
|
5
|
+
how its observed usage is priced.
|
|
6
|
+
|
|
7
|
+
Mortar keeps five concerns separate. Do not collapse them into one:
|
|
8
|
+
|
|
9
|
+
1. A **capability** is an open-ended user or operator outcome exposed by API,
|
|
10
|
+
SDK, CLI, MCP, or control-plane tooling. There is deliberately no fixed
|
|
11
|
+
capability count.
|
|
12
|
+
2. A **trust surface** says which credential and runtime may call a capability.
|
|
13
|
+
3. An **application module** implements and composes use cases.
|
|
14
|
+
4. A **primitive** is a provider-neutral resource, replacement, operational,
|
|
15
|
+
and cost-attribution boundary.
|
|
16
|
+
5. **Usage accounting** observes selected operations, applies meters, quotas,
|
|
17
|
+
and pricing policy, and records the customer ledger.
|
|
18
|
+
|
|
19
|
+
An `internal/application/<name>` Go package is an application composition
|
|
20
|
+
module, not a closed product taxonomy. A capability may span multiple
|
|
21
|
+
application, primitive, account, or API packages, and one application module
|
|
22
|
+
may support several capabilities.
|
|
23
|
+
|
|
24
|
+
The current application modules are `auth`, `cron`, `data`, `files`, `queue`,
|
|
25
|
+
`realtime`, and `secret`. Functions, comms, analytics,
|
|
26
|
+
accounts, and custom domains remain capabilities even when their implementation
|
|
27
|
+
spans primitive, API, account, or other packages rather than a same-named
|
|
28
|
+
application directory. `internal/usage` separately owns metering, quota,
|
|
29
|
+
pricing, and the credit ledger. `internal/subscription` separately owns account
|
|
30
|
+
subscription periods, fulfillment, settlement, and compute-grant allocation;
|
|
31
|
+
usage accounting consumes those grants without owning the subscription
|
|
32
|
+
lifecycle.
|
|
33
|
+
|
|
34
|
+
## The 9 primitives
|
|
35
|
+
|
|
36
|
+
| Primitive | Role |
|
|
37
|
+
|---|---|
|
|
38
|
+
| `compute` | Baseline tenant share of pool compute |
|
|
39
|
+
| `database` | PostgreSQL baseline and durable relational state |
|
|
40
|
+
| `cache` | Redis/in-process cache, rate limits, pub/sub, and presence |
|
|
41
|
+
| `storage` | Object bytes and GB-month storage |
|
|
42
|
+
| `network` | Provider-observed or response-writer outbound bytes |
|
|
43
|
+
| `function` | User function execution through a configured runner |
|
|
44
|
+
| `comms` | Transactional email and SMS delivery |
|
|
45
|
+
| `warehouse` | High-cardinality analytics events and summaries |
|
|
46
|
+
| `log` | Managed provider-log ingestion, indexing, and retention |
|
|
47
|
+
|
|
48
|
+
These names are stable because they align resource ownership, cost attribution,
|
|
49
|
+
and swappable cloud backends. They do not predict the public SDK or customer
|
|
50
|
+
invoice shape. For example, `network` is a side effect rather than
|
|
51
|
+
`mortar.network`, while auth composes database, cache, comms, and network
|
|
52
|
+
without becoming another primitive.
|
|
53
|
+
|
|
54
|
+
Primitive use is not equivalent to a customer charge. The same `warehouse`
|
|
55
|
+
primitive backs both uncharged summary reads and metered event ingestion.
|
|
56
|
+
Provider cost, observed usage, included allowance, and customer price are four
|
|
57
|
+
different facts; `internal/usage` connects them without making primitive
|
|
58
|
+
interfaces depend on plans or billing.
|
|
59
|
+
|
|
60
|
+
## Current capability registry
|
|
61
|
+
|
|
62
|
+
| Capability family | Public app bundle | Trusted project/operator | Account control plane |
|
|
63
|
+
|---|---|---|---|
|
|
64
|
+
| Auth | Signup/signin, OTP, session, reset | User administration | Account signup/signin and notification email |
|
|
65
|
+
| Data | Row CRUD under access policy | Schema/index/column changes and cross-table transactions | Project ownership and lifecycle |
|
|
66
|
+
| Files | Upload/list/delete/sign under bucket policy | Bucket policy and cleanup operations | Project lifecycle cleanup |
|
|
67
|
+
| Realtime | Row changes, broadcast, presence | Backend/channel configuration | — |
|
|
68
|
+
| Functions | Invoke deployed functions | List/deploy/delete/read logs | Read project function logs |
|
|
69
|
+
| Analytics | Capture events and errors | Read project summaries | — |
|
|
70
|
+
| Automation | — | Cron CRUD and queue enqueue/list/get | Read project cron schedules |
|
|
71
|
+
| Communications | — | Send configured email/SMS | Operational account email |
|
|
72
|
+
| Secrets | — | Encrypted project secret CRUD and function injection | — |
|
|
73
|
+
| Usage and logs | — | Project usage ledger and audit logs | Read project usage |
|
|
74
|
+
| Identity and access | — | Project API-key administration | Projects, keys, PATs, ownership |
|
|
75
|
+
| Domains and commerce | — | — | Domain claim/verification, plans, top-up, auto-top-up, capacity/enterprise intent |
|
|
76
|
+
|
|
77
|
+
The registry grows when a real API or workflow ships. Cron, queue, and custom
|
|
78
|
+
domains are implemented capabilities, not a future three-item extension list.
|
|
79
|
+
Their availability can still depend on configuration: cron may be disabled,
|
|
80
|
+
queue may use PostgreSQL or be off, analytics needs a warehouse, functions need
|
|
81
|
+
a runner, comms needs a provider, and domain TLS routing is completed by the
|
|
82
|
+
deployment edge after Mortar verifies ownership.
|
|
83
|
+
|
|
84
|
+
## Trust surfaces
|
|
85
|
+
|
|
86
|
+
- **Public app bundle**: a PUBLIC bundle key plus optional app-user session.
|
|
87
|
+
Fabric's injected `MortarClient` intentionally contains only auth, row data,
|
|
88
|
+
storage, realtime, function invocation, and analytics capture.
|
|
89
|
+
- **Trusted project/operator**: app/admin project keys in server, CI, CLI, MCP,
|
|
90
|
+
or platform provisioning. Never ship these credentials or clients to a Web,
|
|
91
|
+
Desktop renderer, Keel/React Native, or WeChat bundle.
|
|
92
|
+
- **Account control plane**: account JWT or `mtr_pat_*` PAT against the apex
|
|
93
|
+
`_accounts` routes. These credentials manage projects and ownership; they are
|
|
94
|
+
not interchangeable with project or app-user credentials.
|
|
95
|
+
|
|
96
|
+
When documenting or implementing a capability, state its trust surface, its
|
|
97
|
+
application implementation, the primitives it consumes, and any metering and
|
|
98
|
+
pricing policy. No one field answers authorization, implementation, operations,
|
|
99
|
+
and billing at the same time.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# App-user authentication reference
|
|
2
|
+
|
|
3
|
+
Read this reference for signup/signin, OTP, password reset, session state, or
|
|
4
|
+
private-user data modeling.
|
|
5
|
+
|
|
6
|
+
Auth sessions are persisted and attached to later database, storage, realtime,
|
|
7
|
+
and compute calls by the platform-owned singleton.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
const { data, error } = await mortar.auth.signUp({ email, password });
|
|
11
|
+
const signedIn = await mortar.auth.signInWithPassword({ email, password });
|
|
12
|
+
|
|
13
|
+
const sent = await mortar.auth.signInWithOtp({ email }); // or { phone }
|
|
14
|
+
const verified = await mortar.auth.verifyOtp({ email, token });
|
|
15
|
+
|
|
16
|
+
const { data: sessionData } = await mortar.auth.getSession();
|
|
17
|
+
const { data: userData, error: userError } = await mortar.auth.getUser();
|
|
18
|
+
await mortar.auth.signOut();
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Check each returned `error`; do not infer authentication from the presence of UI
|
|
22
|
+
state. Use `onAuthStateChange` when the UI must react to session changes and
|
|
23
|
+
unsubscribe during component/page cleanup.
|
|
24
|
+
|
|
25
|
+
Password reset starts with `mortar.auth.resetPasswordForEmail(email)` and is
|
|
26
|
+
confirmed with `passwordResetConfirm(email, token, newPassword)`. Never store or
|
|
27
|
+
pass session tokens manually unless the user explicitly needs a trusted server
|
|
28
|
+
integration.
|
|
29
|
+
|
|
30
|
+
For private user rows, include the authenticated user ID in the schema/data
|
|
31
|
+
model and select a server-enforced private or owner-scoped table access policy.
|
|
32
|
+
Client-side filters improve UX but do not replace authorization. Anonymous
|
|
33
|
+
intake uses `public_insert`, not broad `public` access.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Cron and queue automation reference
|
|
2
|
+
|
|
3
|
+
Read this reference for scheduled functions, background jobs, retries, workers,
|
|
4
|
+
or atomic row-plus-job workflows. Cron and queue are shipped capabilities whose
|
|
5
|
+
runtime availability depends on configuration; they are not “planned extension
|
|
6
|
+
slots.”
|
|
7
|
+
|
|
8
|
+
Fabric provisions schedules only through one `mortar_migrate` operation:
|
|
9
|
+
`upsert_cron` creates or updates a schedule for an already deployed function,
|
|
10
|
+
and `delete_cron` removes it. Direct cron list/create/patch/delete endpoints are
|
|
11
|
+
trusted operator surfaces. Cron is normally enabled and can be disabled by the
|
|
12
|
+
Mortar deployment.
|
|
13
|
+
|
|
14
|
+
Direct queue enqueue/list/get is also trusted project/operator functionality;
|
|
15
|
+
it is intentionally absent from the public app-bundle `MortarClient`. Queue may
|
|
16
|
+
use the PostgreSQL backend or be disabled. Workers must make handlers
|
|
17
|
+
idempotent, bound retries, and distinguish a claim from successful completion.
|
|
18
|
+
|
|
19
|
+
When a database change and queue enqueue must commit together, use the trusted
|
|
20
|
+
`DatabaseTransactions` API described in `database.md`. That guarantee applies
|
|
21
|
+
only to the PostgreSQL queue backend and the row operations inside the same
|
|
22
|
+
tenant transaction. Function execution, communications, object storage, and
|
|
23
|
+
external providers remain outside that transaction.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Transactional communications reference
|
|
2
|
+
|
|
3
|
+
Read this reference for email, SMS, OTP delivery, or provider behavior. Mortar's
|
|
4
|
+
`comms` primitive records email and SMS as separate ledger resources because
|
|
5
|
+
their provider prices differ.
|
|
6
|
+
|
|
7
|
+
Direct `/comms/send` is a trusted project/operator capability. It is not exposed
|
|
8
|
+
on Fabric's public app-bundle singleton; never place an app/admin key or provider
|
|
9
|
+
credential in client code to reach it. App-user auth may internally compose the
|
|
10
|
+
same primitive for phone OTP, email verification, and password reset.
|
|
11
|
+
|
|
12
|
+
Hosted Mortar can use configured Aliyun SMS and Aliyun DirectMail adapters.
|
|
13
|
+
Provider availability is deployment-specific, and an unconfigured channel must
|
|
14
|
+
fail visibly. Treat delivery as an external side effect: use an idempotency key
|
|
15
|
+
or persisted outbox state where duplicate messages matter, and never claim that
|
|
16
|
+
a database transaction atomically includes provider delivery.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Account and project control-plane reference
|
|
2
|
+
|
|
3
|
+
Read this reference for Mortar accounts, projects, API keys, PATs, custom
|
|
4
|
+
domains, plans, top-up, auto-top-up, capacity, or enterprise placement.
|
|
5
|
+
|
|
6
|
+
The control plane uses the apex host and an account JWT or `mtr_pat_*` personal
|
|
7
|
+
access token. It manages account identity, project lifecycle, project API keys,
|
|
8
|
+
PATs, project usage, domain claims, plan/top-up settings, and enterprise intent.
|
|
9
|
+
These credentials are not app-user sessions and cannot be substituted for a
|
|
10
|
+
project key on tenant data-plane routes.
|
|
11
|
+
|
|
12
|
+
Project API keys have their own scopes. Public bundle keys are expected in app
|
|
13
|
+
code; app/admin keys belong only in trusted server, CI, CLI, MCP, or platform
|
|
14
|
+
provisioning. Revoke and rotate keys through the control plane rather than
|
|
15
|
+
editing generated client configuration.
|
|
16
|
+
|
|
17
|
+
Custom-domain creation records a claim and verification proves DNS ownership.
|
|
18
|
+
Mortar does not by itself finish edge routing or ACME issuance: the deployment
|
|
19
|
+
edge/operator consumes verified state and configures TLS. Do not claim a domain
|
|
20
|
+
is live merely because its TXT verification succeeded.
|
|
21
|
+
|
|
22
|
+
Top-up and capacity changes are commercial/control-plane operations, not schema
|
|
23
|
+
migrations. They may involve provider webhooks and asynchronous placement;
|
|
24
|
+
verify the resulting project state rather than assuming request acceptance is
|
|
25
|
+
final settlement or capacity activation.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Database and transaction reference
|
|
2
|
+
|
|
3
|
+
Read this reference for row CRUD, batch mutations, cross-table transactions, or
|
|
4
|
+
questions about Mortar's exact ACID boundary.
|
|
5
|
+
|
|
6
|
+
## Database SDK
|
|
7
|
+
|
|
8
|
+
The query surface is Supabase-shaped. Async row methods normally return
|
|
9
|
+
`{ data, error }`; check `error` rather than assuming success.
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
const { data: todos, error } = await mortar
|
|
13
|
+
.from('todos')
|
|
14
|
+
.select('*')
|
|
15
|
+
.eq('done', false)
|
|
16
|
+
.order('created_at', { ascending: false })
|
|
17
|
+
.limit(20);
|
|
18
|
+
if (error) throw new Error(error.message);
|
|
19
|
+
|
|
20
|
+
const { data: created, error: insertError } = await mortar
|
|
21
|
+
.from('todos')
|
|
22
|
+
.insert({ title: 'Ship', done: false, user_id: userId })
|
|
23
|
+
.select()
|
|
24
|
+
.single();
|
|
25
|
+
if (insertError) throw new Error(insertError.message);
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Updates and deletes use the same filter chain. Useful operators include `eq`,
|
|
29
|
+
`neq`, ranges, `like/ilike`, `in`, `is`, `order`, `limit`, `single`, and
|
|
30
|
+
`maybeSingle`.
|
|
31
|
+
|
|
32
|
+
## Atomic row mutations
|
|
33
|
+
|
|
34
|
+
Pass an array when every insert or upsert must succeed or fail together. Mortar
|
|
35
|
+
sends at most 200 rows in one request and executes the batch in one
|
|
36
|
+
tenant-scoped PostgreSQL transaction. For upsert, a row with an `id` replaces
|
|
37
|
+
that row's data; a row without an `id` inserts. Do not use a loop or
|
|
38
|
+
`Promise.all` when partial completion would break an invariant.
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
const { data: createdTodos, error: batchError } = await mortar
|
|
42
|
+
.from('todos')
|
|
43
|
+
.insert([
|
|
44
|
+
{ title: 'Prepare', done: false, user_id: userId },
|
|
45
|
+
{ title: 'Ship', done: false, user_id: userId },
|
|
46
|
+
])
|
|
47
|
+
.select();
|
|
48
|
+
if (batchError) throw new Error(batchError.message);
|
|
49
|
+
|
|
50
|
+
const { data: changedTodos, error: updateError } = await mortar
|
|
51
|
+
.from('todos')
|
|
52
|
+
.update({ done: true })
|
|
53
|
+
.eq('user_id', userId)
|
|
54
|
+
.eq('archived', false)
|
|
55
|
+
.select();
|
|
56
|
+
if (updateError) throw new Error(updateError.message);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Filtered `update` and `delete` select and mutate all matching rows inside one
|
|
60
|
+
server-side transaction. Do not pre-select IDs and update/delete them one at a
|
|
61
|
+
time; that introduces a race and loses the all-or-nothing guarantee. Chain
|
|
62
|
+
`.select()` only when the app needs the affected rows returned.
|
|
63
|
+
|
|
64
|
+
## Cross-table transactions are server-only
|
|
65
|
+
|
|
66
|
+
`DatabaseTransactions` can atomically combine up to 200 cross-table row
|
|
67
|
+
`insert`, by-ID `update`, by-ID `delete`, and PostgreSQL-backed queue `enqueue`
|
|
68
|
+
operations. It is intentionally absent from the injected `mortar` singleton and
|
|
69
|
+
requires an `app` or `admin` key. `execute` throws on failure, so use
|
|
70
|
+
`try`/`catch`, not a `{ data, error }` envelope.
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { DatabaseTransactions } from '@mortar-ai/client';
|
|
74
|
+
|
|
75
|
+
const transactions = new DatabaseTransactions(serverOnlyMortarOptions);
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const results = await transactions.execute([
|
|
79
|
+
{ op: 'insert', table: 'orders', data: { customer_id: customerId, total } },
|
|
80
|
+
{ op: 'enqueue', type: 'send_receipt', payload: { customer_id: customerId } },
|
|
81
|
+
]);
|
|
82
|
+
console.log(results);
|
|
83
|
+
} catch (error) {
|
|
84
|
+
// Nothing committed. Report or retry the whole idempotent operation.
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Never instantiate `DatabaseTransactions` in Web, Desktop renderer,
|
|
90
|
+
Keel/React Native, or WeChat code. If a Fabric project has no approved trusted
|
|
91
|
+
server path, report cross-table ACID as unsupported instead of simulating it
|
|
92
|
+
with public-client calls. Transactional queue enqueue also requires Mortar's
|
|
93
|
+
PostgreSQL queue backend; otherwise no row operation commits.
|
|
94
|
+
|
|
95
|
+
## Exact ACID boundary
|
|
96
|
+
|
|
97
|
+
Mortar provides ACID only for work executed in the same tenant-scoped
|
|
98
|
+
PostgreSQL transaction: atomic row batches, filtered row mutations, and the
|
|
99
|
+
trusted cross-table row + PostgreSQL queue API above. Object storage,
|
|
100
|
+
compute/functions, communications providers, analytics delivery, and other
|
|
101
|
+
external systems do not participate. Never claim two-phase commit across them.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Functions reference
|
|
2
|
+
|
|
3
|
+
Read this reference for deploying or invoking compute functions and reading
|
|
4
|
+
function logs. Cron is in `automation.md`; analytics is in `analytics.md`.
|
|
5
|
+
|
|
6
|
+
## Deploy and invoke functions
|
|
7
|
+
|
|
8
|
+
Deploy or delete functions only through one `mortar_migrate` operation with
|
|
9
|
+
kind `deploy_function` or `delete_function`. Do not edit `mortar.config.json`,
|
|
10
|
+
write migration files, or call operator/admin SDK methods from application
|
|
11
|
+
code. Function list/deploy/delete/logs are trusted project or account/operator
|
|
12
|
+
surfaces; the app-bundle client exposes invocation only.
|
|
13
|
+
|
|
14
|
+
Client bundles may invoke a function that the platform already deployed:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
const response = await mortar.compute.invoke({
|
|
18
|
+
name: 'create-checkout',
|
|
19
|
+
method: 'POST',
|
|
20
|
+
body: JSON.stringify({ orderId }),
|
|
21
|
+
contentType: 'application/json',
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
if (!response.ok) {
|
|
25
|
+
throw new Error(`Function failed: ${response.status} ${await response.text()}`);
|
|
26
|
+
}
|
|
27
|
+
const result = await response.json();
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`compute.invoke` is the only public-key compute operation. Mortar infrastructure
|
|
31
|
+
errors such as unavailable compute or timeout throw; user-function status codes
|
|
32
|
+
are returned as the raw `Response`, so check `response.ok` and consume the body.
|
|
33
|
+
Function deployment/logs remain operator-side capabilities and must never be
|
|
34
|
+
recreated with an app/admin key in a browser or mobile bundle.
|
|
35
|
+
|
|
36
|
+
Functions and external providers do not join the caller's PostgreSQL
|
|
37
|
+
transaction. Make externally visible operations idempotent and persist explicit
|
|
38
|
+
pending/succeeded/failed state when a workflow must recover.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Realtime, broadcast, and presence reference
|
|
2
|
+
|
|
3
|
+
Read this reference for row-change notifications, collaboration events, online
|
|
4
|
+
presence, reconnect behavior, or channel cleanup.
|
|
5
|
+
|
|
6
|
+
```ts
|
|
7
|
+
const room = mortar
|
|
8
|
+
.channel('todos-room', { presence: { key: userId } })
|
|
9
|
+
.on(
|
|
10
|
+
'postgres_changes',
|
|
11
|
+
{ event: '*', schema: 'public', table: 'todos' },
|
|
12
|
+
(payload) => refreshFromChange(payload),
|
|
13
|
+
)
|
|
14
|
+
.on('broadcast', { event: 'cursor' }, ({ payload }) => drawCursor(payload))
|
|
15
|
+
.on('presence', { event: 'sync' }, () => renderRoster(room.presenceState()))
|
|
16
|
+
.subscribe((status) => {
|
|
17
|
+
if (status === 'SUBSCRIBED') void room.track({ online: true });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
await room.send({ type: 'broadcast', event: 'cursor', payload: { x, y } });
|
|
21
|
+
|
|
22
|
+
// On component/page cleanup:
|
|
23
|
+
mortar.removeChannel(room);
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Persist durable state through `mortar.from(table)`. Realtime only notifies
|
|
27
|
+
peers; broadcast and presence are not a database and must not be the sole copy
|
|
28
|
+
of business data.
|
|
29
|
+
|
|
30
|
+
Use a stable presence key for the signed-in user, keep cursor/ephemeral payloads
|
|
31
|
+
small, and remove channels when their component/page unmounts. The SDK retains
|
|
32
|
+
the last tracked presence state and re-tracks it after reconnect, but the UI
|
|
33
|
+
must still tolerate disconnect/reconnect and duplicate change notifications.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Project secrets reference
|
|
2
|
+
|
|
3
|
+
Read this reference for function credentials, API tokens, encryption, or secret
|
|
4
|
+
rotation. Secret list/put/delete is a trusted project/operator capability and is
|
|
5
|
+
intentionally absent from the public app-bundle client.
|
|
6
|
+
|
|
7
|
+
Store provider credentials through Mortar's secret provisioning surface. Mortar
|
|
8
|
+
encrypts project secret values at rest and injects the resolved values into the
|
|
9
|
+
configured function environment. Do not copy a secret into Fabric source,
|
|
10
|
+
generated integration files, public environment variables, logs, analytics, or
|
|
11
|
+
function request payloads merely to make it reachable.
|
|
12
|
+
|
|
13
|
+
Application bundles should call a deployed function that uses the secret on the
|
|
14
|
+
server side. Rotation is a separate operation from function invocation; design
|
|
15
|
+
long-running workflows to tolerate a credential changing between attempts. If
|
|
16
|
+
the deployment has no encryption key or secret service configured, report the
|
|
17
|
+
platform failure instead of falling back to plaintext storage.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# File storage and consistency reference
|
|
2
|
+
|
|
3
|
+
Read this reference for buckets, uploads/downloads, signed URLs, media rendering,
|
|
4
|
+
CORS, or workflows that relate a database row to an object.
|
|
5
|
+
|
|
6
|
+
```ts
|
|
7
|
+
const bucket = mortar.storage.from('avatars');
|
|
8
|
+
|
|
9
|
+
const { data: uploaded, error: uploadError } = await bucket.upload(path, file, {
|
|
10
|
+
contentType: file.type,
|
|
11
|
+
upsert: true,
|
|
12
|
+
});
|
|
13
|
+
const { data: blob, error: downloadError } = await bucket.download(path);
|
|
14
|
+
const { data: files, error: listError } = await bucket.list('users/');
|
|
15
|
+
const { data: signed, error: signError } = await bucket.createSignedUrl(path, 3600);
|
|
16
|
+
const { error: removeError } = await bucket.remove([path]);
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Applications address files by their stable logical `path`. Mortar stores new
|
|
20
|
+
uploads under immutable internal physical versions, atomically switches its
|
|
21
|
+
PostgreSQL metadata pointer, and schedules orphan/replaced versions for durable
|
|
22
|
+
retryable cleanup. Never construct, persist, or expose an internal physical
|
|
23
|
+
object key. Application projects do not run or author the platform storage
|
|
24
|
+
migration.
|
|
25
|
+
|
|
26
|
+
This protocol prevents Mortar metadata from pointing at a partial upload, but a
|
|
27
|
+
separate application row and an object upload are not one transaction. Generate
|
|
28
|
+
an unguessable logical key. For a simple workflow, upload first and write the
|
|
29
|
+
business row after success. For recoverable workflows, create a row with a
|
|
30
|
+
stable idempotency key and `pending` state, upload, then mark it `ready`; reconcile
|
|
31
|
+
stale rows to `failed` or retry them. Do not run a database write and
|
|
32
|
+
`bucket.upload()` concurrently and call the result atomic.
|
|
33
|
+
|
|
34
|
+
Downloads mint a short-lived URL and fetch the object directly from the storage
|
|
35
|
+
backend. Reuse `signed.signedUrl` until near expiry and prefer it directly in
|
|
36
|
+
`<img>`, `<video>`, or `<audio>` instead of making an unnecessary blob copy.
|
|
37
|
+
Signed-URL minting is excluded from the project's API-concurrency budget, but
|
|
38
|
+
clients should lazy-load media and cache/reuse each URL. Signing authorizes a
|
|
39
|
+
possible download; it does not prove bytes were served and is not an
|
|
40
|
+
object-egress charge. Only provider access logs or billing reconciliation may
|
|
41
|
+
record actual object egress.
|
|
42
|
+
|
|
43
|
+
The optional third `createSignedUrl` argument (`{ signal, timeoutMs }`) bounds
|
|
44
|
+
only the signing request. `bucket.download(path)` uses JavaScript `fetch()` and
|
|
45
|
+
therefore needs a matching bucket CORS rule in browsers. Appunvs-managed Mortar
|
|
46
|
+
provides that contract; self-hosted storage must allow credential-free `GET` and
|
|
47
|
+
`HEAD` from the app origin. Never attach Mortar Authorization or cookies to a
|
|
48
|
+
signed object URL.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Usage, managed logs, and billing reference
|
|
2
|
+
|
|
3
|
+
Read this reference for quota, balance, overage, managed logs, or network/object
|
|
4
|
+
egress attribution. The 9 primitives are infrastructure and cost-attribution
|
|
5
|
+
boundaries; user-facing usage and billing are capabilities built over selected
|
|
6
|
+
observations, meters, allowances, pricing policy, and the credit ledger.
|
|
7
|
+
|
|
8
|
+
A primitive is never inherently free or billable. For example, warehouse event
|
|
9
|
+
ingestion is metered while a warehouse summary read is not. Keep four facts
|
|
10
|
+
separate: provider cost, observed units, included allowance, and customer
|
|
11
|
+
charge. Primitive interfaces remain unaware of those policies.
|
|
12
|
+
|
|
13
|
+
Project usage and audit-log reads are trusted project/operator surfaces, with an
|
|
14
|
+
account-owned project usage view on the account control plane. They are not part
|
|
15
|
+
of Fabric's public bundle singleton. A balance or quota read does not itself
|
|
16
|
+
create a metered observation or customer charge.
|
|
17
|
+
|
|
18
|
+
Do not estimate object egress from signed-URL minting or object size. Signing is
|
|
19
|
+
authorization and the URL may never be used or may be reused many times. Actual
|
|
20
|
+
direct OSS/COS/OBS bytes require provider access logs or billing reconciliation.
|
|
21
|
+
Mortar-served responses can be counted at the response writer, but direct object
|
|
22
|
+
downloads bypass it.
|
|
23
|
+
|
|
24
|
+
Managed-log billing uses provider-canonical ingestion records with durable IDs
|
|
25
|
+
for deduplication; query response bytes are not ingestion. Function logs retain
|
|
26
|
+
their function/source context. Never turn a read query or repeated reconciliation
|
|
27
|
+
scan into duplicate usage rows.
|