Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/30092-platform-skill-push-block.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@objectstack/skills': patch
---

docs(skills): objectstack-platform teaches the stack's `push` block in `references/operations.md` (Mobile push): `transport: 'direct'` with `apns` / `fcm` blocks is the transport served today, each secret is named by `credentialRef` and never written inline, and the hosted relay (also what `{}` reads as) is refused at boot until its sender ships; the `requires` table points `push` at it, and the push eval case asserts the shape

Clause-②: no
Metadata-protocol: none
5 changes: 2 additions & 3 deletions skills/objectstack-platform/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ token through the `CAPABILITY_PROVIDERS` registry in
| Token | Provider package |
|:--|:--|
| `automation` | `@objectstack/service-automation` — flows, and any declarative `connectors:` entry |
| `analytics` `cache` `storage` `queue` `job` `messaging` `realtime` `settings` `sms` `push` | `@objectstack/service-` + the token |
| `analytics` `cache` `storage` `queue` `job` `messaging` `realtime` `settings` `sms` `push` | `@objectstack/service-` + the token ([the `push` block](./references/operations.md#mobile-push)) |
| `marketplace` | `@objectstack/service-package` |
| `audit` `email` `sharing` `approvals` `webhooks` | `@objectstack/plugin-` + the token |
| `pinyin-search` | `@objectstack/plugin-pinyin-search` |
Expand Down Expand Up @@ -331,8 +331,7 @@ Five rules that change what you write:
- **Declaring is a demand.** A capability YOU declared whose provider package is
absent is a hard boot error; one the platform auto-injects for you stays
best-effort (warn and continue).
- **`auth` implies `email`.** Auth callbacks (password reset, email
verification, magic link, invitation) need the mail service, so the CLI
- **`auth` implies `email`.** Auth callbacks need the mail service, so the CLI
appends `email` whenever `auth` is required.
- **Keep `automation` whenever `plugins:` lists a connector** — connector
executors register their provider factories with it, and without it they have
Expand Down
7 changes: 5 additions & 2 deletions skills/objectstack-platform/evals/config-plugins-ops.json
Original file line number Diff line number Diff line change
Expand Up @@ -201,17 +201,20 @@
{
"id": 12,
"prompt": "Turn on push notifications for our `os serve` deployment: our own iOS and Android apps, signed with our APNs key and our Firebase project.",
"expected_output": "`requires: ['push']` on the stack root (the token mounts `@objectstack/service-push`) plus a top-level `push:` key on the stack definition for the transport; no host code, no `PushServicePlugin` in `plugins:`. The APNs signing key and the Firebase service-account JSON stay in the secrets layer (an environment variable in the open tier) and the config names them by reference (`credentialRef`): no `.p8` key, `privateKey` or service-account JSON written inline.",
"expected_output": "`requires: ['push']` on the stack root (the token mounts `@objectstack/service-push`) plus a top-level `push:` key on the stack definition; no host code, no `PushServicePlugin` in `plugins:`. Their own signed apps take `transport: 'direct'`, the transport served today, with an `apns` block (`teamId`, `keyId`, `topic`, `environment`, `credentialRef`) and an `fcm` block (`projectId`, `credentialRef`). The APNs signing key and the Firebase service-account JSON stay in the secrets layer (an environment variable in the open tier) and each `credentialRef` names one: no `.p8` key, `privateKey`, `serviceAccount` or service-account JSON written inline. Not the hosted relay, which the boot refuses until its sender ships.",
"files": [],
"assertions": {
"must_contain": [
"requires",
"'push'",
"push:"
"push:",
"transport: 'direct'",
"credentialRef"
],
"must_not_contain": [
"BEGIN PRIVATE KEY",
"privateKey:",
"serviceAccount:",
"\"private_key\""
]
}
Expand Down
31 changes: 22 additions & 9 deletions skills/objectstack-platform/references/operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ to be re-run when commands are added.
| `os init` | Scaffold a new project (alternative to `npx create-objectstack`) |
| `os dev` | Start the dev server with hot metadata reload. `--seed-admin` (default **on** for plain `os dev`) seeds a loginable dev admin **in-process via the runtime** (env vars `OS_SEED_ADMIN*`) on an **empty** DB only — idempotent, never overwrites an existing account (default `[email protected]` / `admin123`; override with `--admin-email` / `--admin-password`; disable with `--no-seed-admin`). `--fresh` = ephemeral clean OS_HOME/DB, implies `--seed-admin`. The seeded admin is promoted to **platform admin**, so Setup/Studio work on first login. |
| `os dev --ui` | Also mount the bundled Console portal at `/_console/` (there is no separate `os studio` command) |
| `os validate` | Validate `objectstack.config.ts` — Zod protocol schema, CEL/predicate validation (`record.<field>` existence), and widget-binding integrity. Same gates as `os build`, no artifact emitted. See [Verify your work](#verify-your-work). |
| `os validate` | Validate `objectstack.config.ts`: the `os build` gates, no artifact emitted — see [Verify your work](#verify-your-work) |
| `os lint` | Style/convention lint on metadata files |
| `os info` | Print a metadata summary of the config (objects, apps, and other collections; `--json`) |
| `os doctor` | Diagnose common setup issues |
Expand Down Expand Up @@ -39,7 +39,7 @@ os validate # Zod schema + CEL predicates + widget bindings — no artifact
os build # the same three gates, plus emits dist/objectstack.json
```

`os validate` and `os build` run the **same** structural + semantic gates:
Both run the same gates:

1. **Zod protocol schema** — the stack conforms to `@objectstack/spec`.
2. **CEL / predicate validation (ADR-0032)** — every `visible` / `disabled` /
Expand All @@ -49,13 +49,12 @@ os build # the same three gates, plus emits dist/objectstack.json
3. **Widget-binding integrity (ADR-0021)** — every dashboard widget's
`dataset` / `dimensions` / `values` resolves to a declared dataset/field.

Both exit non-zero with a located, corrective message. Use `os validate` as the
fast inner-loop check after editing metadata and `os build` when you need
`dist/`. In a scaffolded project these are `npm run validate` / `npm run build`.
Both exit non-zero with a located, corrective message. In a scaffolded project
these are `npm run validate` / `npm run build`.

**Rule of thumb: never report a metadata change as done until `os validate`
passes.** (`os lint` is a *separate* style/convention pass — naming, labels,
namespace prefixes — and does not replace `os validate`.)
passes.** (`os lint` is a *separate* style/convention pass and does not
replace `os validate`.)

## Ports & networking

Expand Down Expand Up @@ -112,6 +111,22 @@ Port resolution is the same for `os dev` and `os start` (both spawn `os serve`):
> endpoint that `os package install` targets, ship the **Installed Apps** page and
> marketplace Setup nav as plugin metadata.

## Mobile push

`requires: ['push']` reads the top-level `push` key (`PushServiceConfigSchema`).
Served today: `transport: 'direct'` only, one block per vendor, at least one:

```ts
push: { transport: 'direct',
apns: { teamId, keyId, topic: 'com.acme.app', environment: 'production', credentialRef: 'APNS_P8_KEY' },
fcm: { projectId, credentialRef: 'FCM_SA_JSON' } },
```

`credentialRef` names each secret (the `.p8` key, the service-account JSON; an
env-var name in the open tier), never holds it: `privateKey`, `serviceAccount` or
a PEM/JSON value is refused. The relay transport (also what `{}` or no key reads
as) is refused at boot until its sender ships.

## Shipping a plugin

Authoring stops at `kernel.use(plugin)`; these three make it distributable.
Expand Down Expand Up @@ -189,8 +204,6 @@ describe('stack boot', () => {
| Symptom | Likely cause |
|:--------|:-------------|
| `os dev` hangs at "Loading metadata…" | Circular import in `objectstack.config.ts` — run `os validate` |
| `os start` exits with "Port N is already in use" | Intended: production never auto-shifts ports. Free the port or set `PORT=<n>` — see [Ports & networking](#ports--networking) |
| better-auth `Invalid origin` 403 after a port/host change | Port or hostname out of sync with `OS_AUTH_URL` / `OS_TRUSTED_ORIGINS` — see [Ports & networking](#ports--networking) |
| Migrations apply locally but not in cloud | `env` scoping on the dataset excludes the target environment |
| Adapter 404s on auto-generated routes | `enable.apiEnabled: false` on the object, or missing `os build` |
| LiteKernel test passes, ObjectKernel boot fails | Test missed a plugin the CLI auto-registers — compare your test's `use()` list against the `os dev` boot log |
Expand Down
Loading