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
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@

## Unreleased

- Bumped `@objectstack/*` from 12.4.0 to **14.7.0** in `apps/objectos`, so the
runtime image ships the 14.x train (Permission Model v2, enforced object
capability flags, data-lifecycle contract, phone/SMS auth, MCP scope
ceiling). Verified: `objectstack compile`, `tsc --noEmit`, and the runtime
smoke test all pass on 14.7.0.
- Refreshed the docs site for the ObjectStack 13/14 releases: the
Roles page became [Positions](content/docs/configure/permissions/positions.mdx)
(roles/profiles converged per ADR-0090), permissions/record-access/security
pages document private-by-default sharing, audience anchors, delegated
administration and the explain engine, and the changelog page now covers the
12.x–14.x release trains. English + Simplified Chinese updated; other
locales pending the next translation sync.
- Repository re-initialized as the **ObjectOS reference runtime distribution**.
- Previous codebase preserved on branch `legacy/v1` and tag `v1-final`.
- Adopted pnpm + Turborepo monorepo layout: `apps/objectos`, `apps/docs`, `packages/*`.
Expand Down
2 changes: 1 addition & 1 deletion apps/objectos/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ This package intentionally contains **no protocol code**. All schemas,
the kernel, drivers and official plugins come from `@objectstack/*`
packages on npm. Enterprise plugins maintained in this monorepo live under `../../packages/`
and are composed into the runtime stack returned by
[`createObjectOSStack`](https://www.npmjs.com/package/@objectstack/runtime)
[`createStandaloneStack`](https://www.npmjs.com/package/@objectstack/runtime)
in [`objectstack.config.ts`](./objectstack.config.ts).

See the repository [README](../../README.md) for positioning and the
Expand Down
11 changes: 6 additions & 5 deletions apps/objectos/objectstack.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,12 @@ const stack = await createStandaloneStack({
databaseUrl,
});

// @objectstack 12.1 workaround: createStandaloneStack hard-codes
// `api.projectResolution: 'none'` for the single-tenant standalone case, but the
// 12.1 protocol validator's `api.projectResolution` enum only accepts
// required|optional|auto (the field is optional). Since standalone runs with
// `enableProjectScoping: false`, drop the field so compile/validate passes.
// @objectstack workaround (12.1, still required as of 14.7):
// createStandaloneStack hard-codes `api.projectResolution: 'none'` for the
// single-tenant standalone case, but the protocol validator's
// `api.projectResolution` enum only accepts required|optional|auto (the field
// is optional). Since standalone runs with `enableProjectScoping: false`,
// drop the field so compile/validate passes.
const { projectResolution: _drop, ...api } = stack.api as {
projectResolution?: string;
} & Record<string, unknown>;
Expand Down
20 changes: 10 additions & 10 deletions apps/objectos/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@
"type-check": "tsc --noEmit"
},
"dependencies": {
"@objectstack/cli": "12.4.0",
"@objectstack/cloud-connection": "12.4.0",
"@objectstack/console": "12.4.0",
"@objectstack/core": "12.4.0",
"@objectstack/driver-memory": "12.4.0",
"@objectstack/driver-sql": "12.4.0",
"@objectstack/metadata": "12.4.0",
"@objectstack/objectql": "12.4.0",
"@objectstack/runtime": "12.4.0",
"@objectstack/spec": "12.4.0",
"@objectstack/cli": "14.7.0",
"@objectstack/cloud-connection": "14.7.0",
"@objectstack/console": "14.7.0",
"@objectstack/core": "14.7.0",
"@objectstack/driver-memory": "14.7.0",
"@objectstack/driver-sql": "14.7.0",
"@objectstack/metadata": "14.7.0",
"@objectstack/objectql": "14.7.0",
"@objectstack/runtime": "14.7.0",
"@objectstack/spec": "14.7.0",
"pg": "^8.0.0"
},
"devDependencies": {
Expand Down
41 changes: 37 additions & 4 deletions content/docs/build/data-model.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -206,21 +206,54 @@ ObjectSchema.create({
});
```

## Lifecycle & ownership
## Capability flags & ownership

```ts
ObjectSchema.create({
name: 'task',
ownership: 'own', // 'own' | 'shared' | 'system'
sharingModel: 'private', // OWD — custom objects default to private (v13)
enable: {
apiEnabled: true, // generated REST endpoints
trackHistory: true, // audit log of field changes
feeds: true, // sys_comment / sys_activity / @mentions
trackHistory: true, // History tab + field-level diffs (default true)
feeds: true, // sys_comment / @mentions (default true)
activities: true, // mirror CRUD to sys_activity timeline (default true)
files: true, // Attachments panel (opt-in, default false)
softDelete: true, // tombstone instead of hard delete
},
});
```

Since ObjectStack 14 the `enable.*` flags are **enforced**, not
advisory: `feeds: false` rejects comment creation with 403
`FEEDS_DISABLED`, `files` must be opted in before `sys_attachment` rows
can be created (403 `FILES_DISABLED` otherwise), and `activities` /
`trackHistory` gate the timeline and History tab. The compliance
`sys_audit_log` row is always written regardless of flags.

## Data lifecycle (retention)

High-volume objects can declare a `lifecycle` block so the platform
bounds their growth (ObjectStack 14.4, ADR-0057):

```ts
ObjectSchema.create({
name: 'my_event',
lifecycle: {
class: 'event', // 'record' | 'audit' | 'telemetry' | 'transient' | 'event'
retention: '14d', // reaper deletes rows past the window
storage: { rotation: 'weekly' }, // time-sharded tables, O(1) expiry
},
});
```

The built-in LifecycleService (disable with `OS_LIFECYCLE_DISABLED=1`)
reaps expired rows, rotates time-sharded tables, and archives
audit-class objects. Platform objects such as `sys_activity` (14 days)
and `sys_audit_log` (90 days hot, then archive) ship with lifecycle
declarations you can tune per environment via the
`lifecycle.retention_overrides` setting.

## System objects (free with every project)

You don't have to declare these — they're always there:
Expand All @@ -230,7 +263,7 @@ You don't have to declare these — they're always there:
| `sys_user` | User accounts |
| `sys_org` | Organizations / tenants |
| `sys_member` | Org membership |
| `sys_role`, `sys_permission_set` | RBAC primitives |
| `sys_position`, `sys_permission_set` | RBAC primitives |
| `sys_audit_log` | Audit trail (when audit capability loaded) |
| `sys_file`, `sys_attachment` | File metadata (when storage loaded) |
| `sys_comment`, `sys_activity` | Feed / chatter (when feed loaded) |
Expand Down
37 changes: 33 additions & 4 deletions content/docs/build/data-model.zh-Hans.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -197,21 +197,50 @@ ObjectSchema.create({
});
```

## 生命周期与所有权
## 能力开关与所有权

```ts
ObjectSchema.create({
name: 'task',
ownership: 'own', // 'own' | 'shared' | 'system'
sharingModel: 'private', // OWD——自定义对象默认私有(v13)
enable: {
apiEnabled: true, // 生成 REST 端点
trackHistory: true, // 字段变更审计日志
feeds: true, // sys_comment / sys_activity / @提及
trackHistory: true, // History 选项卡 + 字段级差异(默认 true)
feeds: true, // sys_comment / @提及(默认 true)
activities: true, // CRUD 镜像到 sys_activity 时间线(默认 true)
files: true, // 附件面板(需显式开启,默认 false)
softDelete: true, // 软删除而非硬删除
},
});
```

自 ObjectStack 14 起,`enable.*` 开关是**强制执行**的,而不再只是声明:
`feeds: false` 会以 403 `FEEDS_DISABLED` 拒绝评论创建;`files` 必须显式开启
后才能创建 `sys_attachment` 行(否则返回 403 `FILES_DISABLED`);
`activities` / `trackHistory` 控制时间线和 History 选项卡。无论开关如何,
合规用途的 `sys_audit_log` 行始终会写入。

## 数据生命周期(保留策略)

大数据量对象可以声明 `lifecycle` 块,让平台约束其增长(ObjectStack 14.4,ADR-0057):

```ts
ObjectSchema.create({
name: 'my_event',
lifecycle: {
class: 'event', // 'record' | 'audit' | 'telemetry' | 'transient' | 'event'
retention: '14d', // 回收器删除超出窗口的行
storage: { rotation: 'weekly' }, // 按时间分片表,O(1) 过期
},
});
```

内置的 LifecycleService(可用 `OS_LIFECYCLE_DISABLED=1` 关闭)负责回收过期行、
轮换分片表,并归档 audit 类对象。`sys_activity`(14 天)、`sys_audit_log`
(热存 90 天后归档)等平台对象自带生命周期声明,可通过
`lifecycle.retention_overrides` 设置按环境调整。

## 系统对象(每个项目免费自带)

这些你不必声明 —— 它们一直都在:
Expand All @@ -221,7 +250,7 @@ ObjectSchema.create({
| `sys_user` | 用户账户 |
| `sys_org` | 组织 / 租户 |
| `sys_member` | 组织成员关系 |
| `sys_role`、`sys_permission_set` | RBAC 原语 |
| `sys_position`、`sys_permission_set` | RBAC 原语 |
| `sys_audit_log` | 审计轨迹(加载审计能力时) |
| `sys_file`、`sys_attachment` | 文件元数据(加载存储能力时) |
| `sys_comment`、`sys_activity` | Feed / chatter(加载 feed 能力时) |
Expand Down
25 changes: 24 additions & 1 deletion content/docs/configure/api-access.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,14 @@ Both are enforced at the REST layer — see

## Authentication options

Callers can authenticate in three ways:
Callers can authenticate in four ways:

| Method | Best for | How |
|---|---|---|
| Session cookie | Browser/UI traffic | Sign in through `/api/v1/auth/*`; cookies are scoped to the project hostname |
| Bearer access token | Mobile, SPA, short-lived server jobs | Exchange credentials at `/api/v1/auth/sign-in/email` and pass `Authorization: Bearer <token>` |
| API key | Server-to-server, ETL, long-lived integrations | Create a `sys_api_key`, pass it as a bearer token |
| OAuth 2.1 (MCP) | AI agents / MCP clients acting as a signed-in user | Self-serve authorization-code + PKCE flow against the deployment itself |

All three pass through the same `AuthPlugin` and resolve to a `sys_user`
context that the `SecurityPlugin` evaluates against permissions and
Expand Down Expand Up @@ -95,6 +96,28 @@ To revoke a key, run the `revoke_api_key` action on the corresponding
`sys_api_key` record (also available in the Console UI). Revocation takes
effect immediately on the next request.

## MCP clients & OAuth 2.1 (ObjectStack 13+)

Any OAuth-capable MCP client — Claude.ai custom connectors, Claude
Desktop, Claude Code — can connect **self-serve**, without an admin
minting API keys:

- Each deployment acts as its own authorization server (backed by the
embedded auth instance), with RFC 8414 / RFC 9728 discovery, dynamic
client registration (RFC 7591), authorization-code + PKCE, and
resource binding to `<origin>/api/v1/mcp`. TLS is required
(localhost exempt).
- Users sign in as themselves; tool calls run under their own
permissions and row-level security.
- OAuth scopes establish a hard permission ceiling
(ObjectStack 14.5): `effective_permission = scope_ceiling ∩
user_grants`. `data:read` grants read-only data access, `data:write`
full CRUD, and `actions:execute` is required for capability-gated
business actions. Tokens missing a scope fail closed.

API keys remain a parallel track for CI and headless agents — `x-api-key`
and `Authorization: Bearer osk_…` continue to work unchanged.

## Pagination, filtering, and sorting

Generated list endpoints accept the standard ObjectStack query
Expand Down
15 changes: 13 additions & 2 deletions content/docs/configure/api-access.zh-Hans.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,16 @@ API 暴露在 artifact 中按对象逐个治理,而不是在网关层:

## 身份验证方式

调用方可以通过三种方式进行身份验证
调用方可以通过四种方式进行身份验证

| 方式 | 适用场景 | 做法 |
|---|---|---|
| Session cookie | 浏览器/UI 流量 | 通过 `/api/v1/auth/*` 登录;cookie 的作用域限定在项目主机名 |
| Bearer 访问令牌 | 移动端、SPA、短时服务端任务 | 在 `/api/v1/auth/sign-in/email` 用凭据换取令牌,并以 `Authorization: Bearer <token>` 传递 |
| API 密钥 | 服务器到服务器、ETL、长期存续的集成 | 创建一条 `sys_api_key` 记录,作为 bearer 令牌使用 |
| OAuth 2.1(MCP) | 以登录用户身份行事的 AI 代理 / MCP 客户端 | 直接对部署本身执行自助授权码 + PKCE 流程 |

这三种方式都会经过同一个 `AuthPlugin`,并最终解析为 `sys_user` 上下文,由 `SecurityPlugin` 据此评估权限和记录访问。
这些方式都会经过同一个 `AuthPlugin`,并最终解析为 `sys_user` 上下文,由 `SecurityPlugin` 据此评估权限和记录访问。

## API 密钥

Expand Down Expand Up @@ -80,6 +81,16 @@ curl https://app.example.com/api/v1/data/account \

要撤销某个密钥,请在对应的 `sys_api_key` 记录上运行 `revoke_api_key` action(Console UI 中也提供)。撤销会在下一次请求时立即生效。

## MCP 客户端与 OAuth 2.1(ObjectStack 13+)

任何支持 OAuth 的 MCP 客户端——Claude.ai 自定义连接器、Claude Desktop、Claude Code——都可以**自助**接入,无需管理员签发 API 密钥:

- 每个部署都充当自己的授权服务器(由内嵌认证实例支撑),支持 RFC 8414 / RFC 9728 发现、动态客户端注册(RFC 7591)、授权码 + PKCE,以及绑定到 `<origin>/api/v1/mcp` 的资源约束。要求 TLS(localhost 豁免)。
- 用户以自己的身份登录;工具调用在其本人的权限和行级安全下运行。
- OAuth scope 构成硬性权限上限(ObjectStack 14.5):`effective_permission = scope_ceiling ∩ user_grants`。`data:read` 授予只读数据访问,`data:write` 授予完整 CRUD,能力门控的业务动作需要 `actions:execute`。缺少 scope 的令牌将失败关闭。

API 密钥仍作为 CI 与无头代理的并行通道——`x-api-key` 和 `Authorization: Bearer osk_…` 继续原样工作。

## 分页、过滤与排序

自动生成的列表端点接受标准的 ObjectStack 查询参数:
Expand Down
30 changes: 30 additions & 0 deletions content/docs/configure/authentication.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,38 @@ support:
- two-factor authentication;
- passkeys/WebAuthn;
- magic links;
- phone number sign-in with SMS OTP (opt-in);
- CLI/browser device flow.

## Phone sign-in & SMS (ObjectStack 14.3+)

Enable phone-number authentication via the `auth.plugins.phoneNumber`
setting. Once on:

- `POST /sign-in/phone-number` accepts phone + password;
- SMS OTP covers sign-in verification and self-service password reset,
with per-number cooldowns, rolling-hour caps, and per-IP rate limits;
- `sys_user` gains unique `phone_number` and `phone_number_verified`
columns; phone-only accounts get a placeholder email.

SMS delivery is provided by `@objectstack/plugin-sms` with Aliyun SMS,
Twilio, or a dev log fallback, configured through the `sms` settings
namespace. Message bodies containing OTP codes are never persisted or
logged. Notifications can also target the `sms` channel via
`notify(channels: ['sms'])`.

## Admin user management (ObjectStack 14.3+)

Platform admins can provision accounts without the email invite flow:

- `POST /api/v1/auth/admin/create-user` creates a user directly, with an
optional one-time password (returned once, never persisted). The
`must_change_password` flag forces rotation at first sign-in (403
`PASSWORD_EXPIRED` until changed).
- `POST /api/v1/auth/admin/import-users` bulk-imports rows / CSV / XLSX
(max 500 rows per request) with dry-run and upsert-by-email-or-phone
modes; existing users never get their passwords reset.

## Required secret

Set:
Expand Down
18 changes: 18 additions & 0 deletions content/docs/configure/authentication.zh-Hans.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,26 @@ ObjectOS 使用由 Better Auth 驱动的 ObjectStack 认证插件。认证是项
- 双因素认证;
- Passkey/WebAuthn;
- 魔法链接;
- 手机号登录与短信 OTP(需显式开启);
- CLI/浏览器设备流程。

## 手机号登录与短信(ObjectStack 14.3+)

通过 `auth.plugins.phoneNumber` 设置开启手机号认证。开启后:

- `POST /sign-in/phone-number` 接受手机号 + 密码登录;
- 短信 OTP 覆盖登录验证和自助密码重置,并自带按号码冷却、滚动小时上限和按 IP 限流;
- `sys_user` 增加唯一的 `phone_number` 与 `phone_number_verified` 列;仅手机号账户会获得占位邮箱。

短信发送由 `@objectstack/plugin-sms` 提供,支持阿里云短信、Twilio 或开发环境日志回退,通过 `sms` 设置命名空间配置。含 OTP 验证码的消息体永不持久化或写入日志。通知也可以通过 `notify(channels: ['sms'])` 走 `sms` 通道。

## 管理员用户管理(ObjectStack 14.3+)

平台管理员可以不经邮件邀请流程直接开通账户:

- `POST /api/v1/auth/admin/create-user` 直接创建用户,可选生成一次性密码(仅返回一次,不持久化)。`must_change_password` 标志强制首次登录时修改密码(未修改前返回 403 `PASSWORD_EXPIRED`)。
- `POST /api/v1/auth/admin/import-users` 批量导入行 / CSV / XLSX(每次请求最多 500 行),支持 dry-run 和按邮箱或手机号 upsert;已存在用户的密码永不重置。

## 必需的 Secret

设置:
Expand Down
2 changes: 1 addition & 1 deletion content/docs/configure/permissions/index.de.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ ausdrücken können wie "der Vorgesetzte des Datensatzeigentümers".
Verwenden Sie Rollen, wenn Sie hierarchischen Zugriff benötigen;
verzichten Sie auf sie bei flachen Teams.

Siehe [Roles](/docs/configure/permissions/roles).
Siehe [Roles](/docs/configure/permissions/positions).

## Schicht 3 — Berechtigungssätze

Expand Down
2 changes: 1 addition & 1 deletion content/docs/configure/permissions/index.es.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ principalmente para que las reglas de uso compartido y los informes puedan decir
gerente del propietario del registro". Usa roles cuando necesites acceso
jerárquico; omítelos para equipos planos.

Consulta [Roles](/docs/configure/permissions/roles).
Consulta [Roles](/docs/configure/permissions/positions).

## Capa 3 — Conjuntos de permisos

Expand Down
2 changes: 1 addition & 1 deletion content/docs/configure/permissions/index.fr.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ propriétaire de l'enregistrement ». Utilisez les rôles lorsque vous avez
besoin d'un accès hiérarchique ; ignorez-les pour les équipes à
structure plate.

Voir [Rôles](/docs/configure/permissions/roles).
Voir [Rôles](/docs/configure/permissions/positions).

## Couche 3 — Ensembles d'autorisations

Expand Down
2 changes: 1 addition & 1 deletion content/docs/configure/permissions/index.ja.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Field security For those records, which FIELDS are readable / writable?

ロールは組織図(CFO → 財務マネージャー → アナリスト)をモデル化します。これらが存在する主な目的は、シェアルールやレポートで「レコード所有者のマネージャー」といった表現を可能にすることです。階層的なアクセスが必要な場合はロールを使い、フラットなチームでは省略してください。

[ロール](/docs/configure/permissions/roles)を参照してください。
[ロール](/docs/configure/permissions/positions)を参照してください。

## レイヤー 3 — 権限セット

Expand Down
2 changes: 1 addition & 1 deletion content/docs/configure/permissions/index.ko.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Field security For those records, which FIELDS are readable / writable?

역할은 조직도(CFO → Finance Manager → Analyst)를 모델링합니다. 역할이 존재하는 주된 이유는 공유 규칙과 보고서가 "레코드 소유자의 관리자"와 같은 표현을 할 수 있게 하기 위함입니다. 계층적 접근이 필요할 때 역할을 사용하고, 평면적인 팀에서는 생략하세요.

[역할](/docs/configure/permissions/roles)을 참조하세요.
[역할](/docs/configure/permissions/positions)을 참조하세요.

## 계층 3 — 권한 집합

Expand Down
Loading
Loading