Skip to content
Merged
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ os doctor # Check environment health
os explain # Explain protocol concepts on the command line
```

Cloud, package registry, and environment management subcommands (`os publish`, `os rollback`, `os package`, `os login`, `os whoami`, `os cloud …`) are available when targeting an ObjectStack Cloud control plane.
Cloud, package registry, and environment management subcommands (`os package publish`, `os package install`, `os login`, `os whoami`, `os environments`, `os cloud …`) are available when targeting an ObjectStack Cloud control plane.

## Use the generated API

Expand Down
17 changes: 8 additions & 9 deletions content/docs/concepts/cloud-artifact-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,15 @@ caches.
}
```

The CLI command is:
This control-plane endpoint is driven by the Cloud / Marketplace install flow.
The framework CLI publishes through the package catalog:

```bash
OS_CLOUD_URL=https://cloud.example.com \
OS_ENVIRONMENT_ID=env_prod \
os publish
os package publish ./dist/objectstack.json --env env_prod --install
```

(The legacy direct-to-environment `os publish` CLI was removed — #2237.)

---

## Activate revision endpoint
Expand All @@ -57,11 +58,9 @@ Activating a revision changes the environment's current artifact pointer.
Runtime nodes can then reload or refresh their environment kernel based on the
new commit/checksum.

```bash
OS_CLOUD_URL=https://cloud.example.com \
OS_ENVIRONMENT_ID=env_prod \
os rollback --commit 9ce1bd48dd7022b8
```
Revision activation is a Cloud control-plane operation. The framework `os rollback`
CLI was removed (#2237); to change an environment's active artifact, install the
desired package version into it.

---

Expand Down
14 changes: 14 additions & 0 deletions content/docs/concepts/metadata-lifecycle.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,20 @@ The `seq` is the single source of truth for ordering. The Studio status badge (`

---

## Package-first authoring (ADR-0070)

Every runtime-authored item lives inside a **writable package** — there are no
orphans. Code-defined and installed packages are **read-only** at runtime, so the
first step of any Studio/API authoring action is to target a *writable* package (a
"base"): a create/update aimed at a read-only package is rejected, and the author
is asked to pick or create a writable base first. New objects, fields, views, and
flows are namespaced into that package — which is exactly what `os package publish`
later ships. See [ADR-0070](https://github.com/objectstack-ai/framework/blob/main/docs/adr/0070-package-first-authoring.md).

> Note: "package" here is the runtime **authoring base** (a `package_id`-bearing
> container of metadata), distinct from the npm `@objectstack/*` packages listed in
> the repo README.

## Overlay whitelist (shared-DB tenancy invariant)

In shared-database multi-tenancy, **most metadata types must not be per-org customizable** — overriding them would break the physical schema. The whitelist lives in **one** place: `MetadataTypeRegistryEntry.allowOrgOverride` in `packages/spec/src/kernel/metadata-plugin.zod.ts`.
Expand Down
2 changes: 1 addition & 1 deletion content/docs/getting-started/core-concepts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ This ensures ObjectStack apps can run on Node.js + PostgreSQL today, Python + SQ
| **Engine** | Enforces the logic | Compiles the `using` condition into a SQL `WHERE` clause |

<Callout type="info">
CRUD permissions (`allowRead`, `allowEdit`, …) are simple booleans — they grant or deny an operation. Record-level *conditional* access is a separate mechanism: [Row-Level Security](/docs/getting-started/architecture) policies, whose `using` clause is a SQL-like (PostgreSQL-compatible) predicate over context variables such as `current_user.role` and `current_user.id`.
CRUD permissions (`allowRead`, `allowEdit`, …) are simple booleans — they grant or deny an operation. Record-level *conditional* access is a separate mechanism: [Row-Level Security](/docs/getting-started/architecture) policies, whose `using` clause is a SQL-like (PostgreSQL-compatible) predicate over context variables such as `current_user.roles` and `current_user.id`.
</Callout>

### Single Source of Truth
Expand Down
5 changes: 5 additions & 0 deletions content/docs/guides/analytics-datasets.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ capability the inline single-object query could never reach:
// → revenue by account.region, joined + RLS-enforced per object.
```

Joins follow **dotted relationship paths** and may traverse **multiple to-one hops**
(ADR-0071) — e.g. `account.owner.region` walks the chain order → account → owner →
region. The path is capped at **3 join hops (4 objects)** and is **to-one only**;
to-many traversal is rejected (aggregate the many-side as its own measure instead).

## How it runs

A dataset compiles to the **Cube analytics runtime** (`IAnalyticsService`). The
Expand Down
6 changes: 6 additions & 0 deletions content/docs/guides/auth-sso.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ Enterprise packages can pass `oidcProviders` into `@objectstack/plugin-auth`
or contribute them through `auth:configure`. The open-source package does not
ship a generic OIDC settings UI.

> **Per-environment external IdP (ADR-0069).** Recent releases add a
> per-environment external-IdP path built on `@better-auth/sso` (a generic OIDC
> relying party) and surface an `sso` flag in the public `/auth/config`
> (`features.sso`) so a client can show an enterprise-login button when SSO is
> configured. The `oidcProviders` extension below remains the in-process path.

### Quick start — Okta

```ts
Expand Down
17 changes: 5 additions & 12 deletions content/docs/guides/cloud-deployment.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -84,18 +84,11 @@ request — no restart, and no environment id to remember.
> install in one call: `os package publish --env <id> --install`. This is for
> automation only — interactive installs happen in the environment Marketplace.

> **Legacy env-revision path.** `os publish` posts compiled metadata straight to
> an environment as a revision, rather than as a versioned package:
>
> ```bash
> OS_CLOUD_URL=https://cloud.example.com OS_ENVIRONMENT_ID=env_prod os publish
> # POST /api/v1/cloud/environments/:environment/metadata
> # rollback: POST /api/v1/cloud/environments/:environment/revisions/:commit/activate
> ```
>
> It still works, but the package flow above is the direction of travel
> (ADR-0006 v4): `sys_environment_revision` is retired once all publishes go
> through the package endpoints.
> **Removed: the legacy env-revision path.** The direct-to-environment `os publish`
> / `os rollback` commands (which wrote `sys_environment_revision`) were **removed**
> (#2237, ADR-0006 v4). All publishing now goes through the package flow above
> (`os package publish`); to change what an environment runs, install a package
> version into it.

Cloud control-plane hosts, database provisioning, billing, and public SaaS
deployment are outside this framework repository. The framework runtime exposes
Expand Down
22 changes: 22 additions & 0 deletions content/docs/guides/data-modeling.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,28 @@ enable: {

---

### Global search — `searchable` + `searchableFields`

`searchable: true` includes an object's records in global search and the record
picker. By default the search matches the name/title field plus short-text fields.
To control exactly which fields are matched, set `searchableFields` on the object
(ADR-0061):

```typescript
{
name: 'account',
searchable: true,
searchableFields: ['name', 'website', 'billing_city'],
}
```

Queries then use the `$search` filter operator to match across `searchableFields`
(e.g. `{ filters: { $search: 'acme' } }`); a view may narrow the set with
`$searchFields`. When `searchableFields` is unset, search falls back to the
name/title field plus short-text fields.

---

## Field Types & Configuration

### Text Fields
Expand Down
2 changes: 1 addition & 1 deletion content/docs/guides/metadata/object.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ indexes: [
| `active` | `boolean` | Is object active (default: `true`) |
| `isSystem` | `boolean` | System object, protected from deletion (default: `false`) |
| `abstract` | `boolean` | Abstract base, cannot be instantiated (default: `false`) |
| `sharingModel` | `enum` | `'private'`, `'read'`, `'read_write'`, `'full'` |
| `sharingModel` | `enum` | Org-Wide Default record visibility (ADR-0055/0056). Canonical: `'private'`, `'public_read'`, `'public_read_write'`, `'controlled_by_parent'` (detail visibility derived from its master). Legacy aliases: `'read'`=public_read, `'read_write'`/`'full'`=public_read_write |
| `keyPrefix` | `string` | Short prefix for record IDs (e.g. `'001'`) |
| `recordTypes` | `string[]` | Record type names for this object |
| `validations` | `ValidationRule[]` | Object-level validation rules (see [Validation](./validation)) |
Expand Down
1 change: 1 addition & 0 deletions content/docs/guides/metadata/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ Components are the building blocks placed inside regions.
| `events` | `Record<string, string>` | optional | Event handlers (action expressions) |
| `style` | `object` | optional | CSS styles |
| `className` | `string` | optional | CSS class names |
| `responsiveStyles` | `object` | optional | **Preferred** SDUI styling channel (ADR-0065): per-breakpoint scoped CSS-property maps keyed by `xs`/`sm`/`md`/`lg`/`xl`/`2xl`, compiled to id-scoped CSS at render. Prefer design tokens, e.g. `{ base: { padding: 'var(--space-6)' }, md: { padding: 'var(--space-8)' } }`. Use over ad-hoc `style`/`className` for metadata-authored pages. |
| `visibility` | `string` | optional | Visibility predicate (CEL expression) |

The `type` field is a union of the standard `PageComponentType` enum and any custom string. The standard (namespaced) component types include:
Expand Down
9 changes: 7 additions & 2 deletions content/docs/guides/metadata/permission.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,13 @@ contextVariables: {
```

The built-in context variables available in RLS `using`/`check` clauses include
`current_user.id`, `current_user.organization_id`, `current_user.department`,
`current_user.role`, and `current_user.region`.
`current_user.id`, `current_user.organization_id`, and `current_user.roles`
(ADR-0068). `roles` is a **string array** — the only canonical role field — so test
membership with CEL: `'org_admin' in current_user.roles` or
`current_user.roles.exists(r, r == 'sales_manager')`. The framework-seeded built-in
role names are `platform_admin`, `org_owner`, `org_admin`, and `org_member`.
(`current_user.isPlatformAdmin` is a derived, deprecated alias of
`'platform_admin' in current_user.roles`.)

## Profile vs. Permission Set

Expand Down
110 changes: 51 additions & 59 deletions content/docs/guides/publish-and-preview.mdx
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
---
title: Publish, Versioning & Preview
description: Compile metadata, publish it to a Cloud environment, and activate or roll back revisions.
description: Compile metadata into an artifact, publish it as a versioned package to your Cloud catalog, optionally install it into an environment, and preview locally.
---

# Publish, Versioning & Preview

ObjectStack treats compiled metadata as an immutable environment artifact. The
local loop is:
ObjectStack treats compiled metadata as an immutable artifact. The local loop is:

```text
objectstack.config.ts -> os compile -> dist/objectstack.json -> os publish -> Cloud environment revision
objectstack.config.ts -> os compile -> dist/objectstack.json -> os package publish -> Cloud package version (optionally installed into an environment)
```

The framework CLI owns compile/publish/rollback commands. The Cloud control
plane that stores revisions and serves them to runtime nodes lives outside this
framework repo.
The framework CLI owns `compile` and `package publish`. The Cloud control plane
that stores package versions, installs them into environments, and serves them to
runtime nodes lives outside this framework repo.

<Callout type="warning">
The legacy direct-to-environment `os publish` / `os rollback` commands (which wrote
`sys_environment_revision`) were **removed** (#2237). Publishing now goes through the
**package catalog**: `os package publish` uploads a versioned package, and an
environment is updated by **installing** a version into it. Revision-style rollback
is no longer a framework CLI command — version and environment management are a
Cloud control-plane concern.
</Callout>

---

Expand All @@ -25,88 +33,72 @@ os compile
# -> dist/objectstack.json
```

The artifact contains metadata, manifest requirements, and packaged function
code. Deployment config stays outside the artifact: database URLs, secrets,
runtime credentials, and environment identity are host inputs.
The artifact contains metadata, manifest requirements, and packaged function code.
Deployment config stays outside the artifact: database URLs, secrets, runtime
credentials, and environment identity are host inputs.

---

## 2. Publish
## 2. Publish a package version

```bash
OS_CLOUD_URL=https://cloud.example.com \
OS_ENVIRONMENT_ID=env_prod \
os publish
```

Common inputs:

| Input | Env var | Purpose |
|:---|:---|:---|
| `--server`, `-s` | `OS_CLOUD_URL` | Cloud control-plane URL |
| `--environment`, `-e` | `OS_ENVIRONMENT_ID` | Target environment id |
| `--token`, `-t` | `OS_CLOUD_API_KEY` | Bearer token when required |
| `--timeout` | `OS_CLOUD_TIMEOUT_MS` | Request timeout |
# upload dist/objectstack.json as a new version in your org catalog
os package publish

The CLI posts the compiled JSON to:

```text
POST /api/v1/cloud/environments/:environment/metadata
# explicit artifact + install into an environment in one step
os package publish ./dist/objectstack.json --env env_prod --install
```

The response includes the environment id, commit id, and checksum for the newly
published revision.

---
The CLI:

## 3. Activate or roll back
1. `POST /api/v1/cloud/packages` — ensure a `sys_package` row exists (id derived from `artifact.manifest.id`, or `--manifest-id`).
2. `POST /api/v1/cloud/packages/:id/versions` — snapshot `dist/objectstack.json` into `sys_package_version.manifest_json` (status `published`).
3. (optional) install the new version into `--env`.

To activate a previous commit:
Common flags:

```bash
OS_CLOUD_URL=https://cloud.example.com \
OS_ENVIRONMENT_ID=env_prod \
os rollback --commit 9ce1bd48dd7022b8
```

The CLI calls:

```text
POST /api/v1/cloud/environments/:environment/revisions/:commit/activate
```

Runtime hosts can then refresh the environment kernel using the active
commit/checksum.
| Flag | Env var | Purpose |
|:---|:---|:---|
| `artifact` (positional) | — | Path to the compiled artifact (default `dist/objectstack.json`) |
| `--server`, `-s` | `OS_CLOUD_URL` | Cloud control-plane URL |
| `--token`, `-t` | `OS_CLOUD_API_KEY` | Bearer token (service mode) |
| `--version`, `-v` | — | Semver version (default: `artifact.manifest.version`) |
| `--visibility` | — | `org` (default) · `private` · `marketplace` |
| `--org` | `OS_ORG_ID` | Owner org id (service mode) |
| `--env` | `OS_ENVIRONMENT_ID` | Environment to install the new version into |
| `--install` | — | Auto-install the new version into `--env` after publishing |

In user mode the package is owned by your active organization; in service mode
(bearer key) pass `--org`. See [Packages](./packages) for the package model and the
[Cloud Environment Artifact API](../concepts/cloud-artifact-api) for the
control-plane endpoints.

---

## 4. Package publish

Packages are separate from environment revisions:

```bash
os package publish --env env_prod
```
## 3. Install / update an environment

Package publish targets the Cloud package registry endpoints, while
`os publish` targets an environment revision.
An environment is updated by **installing a package version** into it — either at
publish time with `--env --install`, or separately via the Cloud control plane /
Marketplace. To "roll back," install the prior version; there is no
revision-activate CLI command anymore.

---

## 5. Preview patterns
## 4. Preview patterns

Use one of these shapes:

| Pattern | Command |
|:---|:---|
| Local artifact preview | `os dev --artifact ./dist/objectstack.json --ui` |
| Production artifact host | `OS_ARTIFACT_PATH=./dist/objectstack.json os start` |
| Cloud environment preview | Publish to a preview environment id, then route clients to that environment via `/api/v1/environments/:environmentId/...` or `X-Environment-Id`. |
| Cloud environment preview | Install to a preview environment, then route clients to it via `/api/v1/environments/:environmentId/...` or `X-Environment-Id`. |

---

## Related

- [Packages](./packages)
- [Deployment Modes](./cloud-deployment)
- [Environment-Scoped Routing](./project-scoping)
- [Cloud Environment Artifact API](../concepts/cloud-artifact-api)
21 changes: 21 additions & 0 deletions content/docs/guides/security.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,27 @@ RLS (no compiler change; ADR-0055). Sharing rules still widen on top.
> resolver); without it they **fail closed to `own`** (never fail-open), and
> `defineStack` errors if a grant uses one without `requires: ['hierarchy-security']`.

### Capabilities & secure-by-default object access (ADR-0066)

Beyond CRUD grants and scope depth, two newer gates compose with the model above:

- **Capabilities** are first-class records in the `sys_capability` registry (ADR-0066 D1) — maintainable named privileges such as `manage_metadata`, `manage_users`, `manage_org_users`, `manage_platform_settings`, `setup.access`, and `studio.access`. Grant them through permission sets like any other privilege.
- **Object access posture** — an object may declare `access: { default: 'public' | 'private' }` (ADR-0066 D2). `public` (the default) preserves allow-by-default exposure; `private` makes the object **secure-by-default** (no access unless explicitly granted).
- **Object-level `requiredPermissions`** (ADR-0066 D3) — a string array of capabilities that gate the object as an **AND** alongside CRUD grants (mirrors `App.requiredPermissions`): a user must hold every listed capability *and* the relevant CRUD grant.

### Built-in identity roles (ADR-0068)

`current_user.roles` is a **string array** — the only canonical role field (there is no singular `current_user.role`). Four role names are framework-seeded into `sys_role`:

| Role | Meaning |
|:---|:---|
| `platform_admin` | Platform operator (SaaS admin) — not a tenant role |
| `org_owner` | Organization owner (`sys_member.role = owner`) |
| `org_admin` | Organization administrator (`sys_member.role = admin`) |
| `org_member` | Organization member (`sys_member.role = member`) |

Test membership in RLS/CEL with `'org_admin' in current_user.roles` or `current_user.roles.exists(r, r == 'sales_manager')`. `current_user.isPlatformAdmin` is a derived, deprecated alias of `'platform_admin' in current_user.roles`.

### Standard Profiles

#### Sales Representative
Expand Down
Loading