Skip to content
Open
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
27 changes: 19 additions & 8 deletions crates/persisting-pchronicle-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,8 +356,11 @@ S3-compatible endpoints can be stored separately with `--endpoint URL` and are
applied as AWS_ENDPOINT_URL_S3 when the alias is used.
HTTP endpoints automatically enable AWS_ALLOW_HTTP for local S3-compatible
services such as MinIO.
An optional `--region REGION` is stored per alias; when omitted, the client
falls back to `us-west-2` only when it needs a region.
An optional `--region REGION` is stored per alias; when omitted for an
`s3://` alias, pChronicle applies `AWS_REGION` / `AWS_DEFAULT_REGION` as
`us-west-2` before opening the store (OpenDAL requires a region).
Endpoint, region, and credentials are applied before the Tokio runtime starts
so local MinIO-style endpoints work without exporting AWS_* in the shell.
A `catalog://127.0.0.1:PORT` alias is a Directory locator: `@team/prod` fetches a
ticket and opens the ticket path. User `--ak/--sk` are required;
`--endpoint` and `--region` are not accepted. Backend object-store keys stay on
Expand Down Expand Up @@ -386,7 +389,7 @@ enum AliasCommand {
/// S3-compatible service endpoint, stored separately from the Dataset URI.
#[arg(long, value_name = "URL")]
endpoint: Option<String>,
/// S3 region. If omitted, the client default (`us-west-2`) is used when needed.
/// S3 region. If omitted for s3:// aliases, defaults to us-west-2.
#[arg(long, value_name = "REGION")]
region: Option<String>,
/// S3 access key ID. Must be provided together with --sk.
Expand Down Expand Up @@ -420,7 +423,7 @@ enum AliasCommand {
/// Replace the S3-compatible service endpoint stored for this alias.
#[arg(long, value_name = "URL")]
endpoint: Option<String>,
/// Replace the S3 region stored for this alias.
/// Replace the S3 region stored for this alias (omit to keep; clear by re-adding).
#[arg(long, value_name = "REGION")]
region: Option<String>,
/// Replace the S3 access key ID stored for this alias.
Expand Down Expand Up @@ -904,7 +907,7 @@ struct ExportArgs {
)
)]
struct ServeArgs {
/// Issue catalog users or change grants without starting Warehouse.
/// Manage Directory ACL or start Warehouse without a catalog subcommand.
#[command(subcommand)]
command: Option<ServeSubcommand>,

Expand Down Expand Up @@ -994,8 +997,10 @@ struct ServeArgs {
#[arg(long = "gateway-debug", alias = "debug", requires = "gateway_config")]
debug: bool,

/// Directory ACL file (libraries + users). Enables catalog:// locators and
/// per-user query workers for the Web API.
/// Directory ACL file (libraries + users). Mounts every [datasets.*] entry
/// into Warehouse and enables catalog:// locators. Mutually exclusive with
/// positional Dataset mounts. Apply S3 endpoint/region/keys from the file
/// before opening stores.
#[arg(
long = "catalog-config",
value_name = "FILE",
Expand All @@ -1010,7 +1015,7 @@ struct ServeArgs {

#[derive(Debug, Subcommand)]
enum ServeSubcommand {
/// Issue catalog users and grant libraries without starting HTTP.
/// Manage Directory ACL (users, grants, datasets) without starting HTTP.
Catalog(CatalogManageArgs),
}

Expand Down Expand Up @@ -1059,16 +1064,22 @@ enum CatalogDatasetCommand {
struct CatalogDatasetAddArgs {
#[command(flatten)]
file: CatalogFileArg,
/// Library / mount name (becomes the Warehouse dataset name).
#[arg(value_name = "NAME")]
name: String,
/// Dataset URI (local path or s3://bucket/prefix).
#[arg(long = "uri", value_name = "URI")]
uri: String,
/// S3-compatible endpoint for this library (required consistency across s3:// entries).
#[arg(long = "endpoint", value_name = "URL")]
endpoint: Option<String>,
/// S3 region for this library (required for s3:// when not relying on process env).
#[arg(long = "region", value_name = "REGION")]
region: Option<String>,
/// Backend object-store access key (not a Directory user key).
#[arg(long = "access-key", value_name = "KEY")]
access_key: Option<String>,
/// Backend object-store secret key (not a Directory user key).
#[arg(long = "secret-key", value_name = "KEY")]
secret_key: Option<String>,
#[arg(long, value_enum, default_value_t = OutputFormat::Auto)]
Expand Down
13 changes: 13 additions & 0 deletions crates/persisting-pchronicle-cli/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,12 +320,25 @@ fn command_tree_contains_the_product_commands() {
.map(|command| command.get_name())
.collect::<Vec<_>>();
assert_eq!(catalog_commands, ["issue", "grant", "revoke", "dataset"]);
let dataset = catalog
.get_subcommands()
.find(|command| command.get_name() == "dataset")
.unwrap();
let dataset_commands = dataset
.get_subcommands()
.map(|command| command.get_name())
.collect::<Vec<_>>();
assert_eq!(dataset_commands, ["add", "remove", "list"]);
let mut serve_command = Cli::command();
let serve_help = serve_command.find_subcommand_mut("serve").unwrap();
let mut help = Vec::new();
serve_help.write_long_help(&mut help).unwrap();
let help = String::from_utf8(help).unwrap();
assert!(help.contains("pchronicle serve catalog"), "{help}");
assert!(
help.contains("Mounts every [datasets.*] entry"),
"{help}"
);
}

#[test]
Expand Down
15 changes: 9 additions & 6 deletions docs/src/en/pchronicle/design/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,12 @@ Snapshot before switching readers. Dataset tables prune by Source before
opening matching fixed versions; caches and routing indexes are tied to that
Snapshot generation.

With `--catalog-config`, the parent process serves Directory list/ticket routes
and does not open those paths itself. Authorized Web queries run in a worker
that only receives the caller's paths. After a CLI ticket, the client opens the
ticket `uri` (a path) with storage credentials. That is platform addressing over
paths, not a new Dataset kind.
With `--catalog-config`, Warehouse mounts every `[datasets.*]` library from the
Directory ACL file (same data plane as positional mounts) and also serves
Directory list/ticket routes for `catalog://` aliases. Backend S3 endpoint,
region, and keys from the file are applied before stores open. After a CLI
ticket, the client opens the ticket `uri` (a path) with storage credentials.
That is platform addressing over paths, not a new Dataset kind.

The Web application and API are consumers of the same read model. They do not
become another source of truth. Unknown API routes remain errors rather than SPA
Expand All @@ -178,7 +179,9 @@ Gateway composition belong to the [`pchronicle` reference](../reference/cli.md).
- [Snapshot design](catalog.md): discovery, Snapshot construction, lazy Source
resolution, and pruning.
- [RFC-0013 path Directory](../../rfcs/0013-pchronicle-warehouse-catalog.md):
name-to-path resolution, ACL, tickets, and query workers.
name-to-path resolution, ACL, and tickets.
- [RFC-0015 `chronicle.manifest`](../../rfcs/0015-chronicle-manifest.md): nested
Dataset discovery and aggregate-stat sidecars.
- [Run storage](trajectory-storage.md): canonical facts, storage layouts, and
write ownership.
- [Storyline Lance](storyline-lance.md): three-table projection, content layer,
Expand Down
8 changes: 8 additions & 0 deletions docs/src/en/pchronicle/design/catalog.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,14 @@ Stopping descent after a composite root is recognized keeps manifests,
generations, segments, and `objects.lance` from being treated as user
input.

When a directory contains `chronicle.manifest`
([RFC-0015](../../rfcs/0015-chronicle-manifest.md)), discovery prefers that
sidecar: a `leaf` with `format = compact-jsonl/v1` becomes a Compact source
without opening Lance solely to classify it; a `branch` scans only immediate
child directories that also have the sidecar. Explorer folder totals may use
leaf `record_count` with read-side roll-up; writers update only the leaf
manifest and do not rewrite ancestors.

### 5.2 Local discovery

Local URIs accept ordinary paths, `local://`, and `file://`:
Expand Down
6 changes: 5 additions & 1 deletion docs/src/en/pchronicle/guides/exchange.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ session JSONL. Export refuses those two formats.
Compact JSONL keeps one JSON object per row without assigning trajectory
semantics. Use `--input-format compact-jsonl` or
`--output-format compact-jsonl`; see the [CLI reference](../reference/cli.md)
for the `--column` mapping and snapshot-sync restrictions.
for the `--column` mapping and snapshot-sync restrictions. Records without a
usable `id` receive a stable `source_filename#line_number` identity; export
preserves original input bytes. Successful compact import also writes a leaf
`chronicle.manifest` at the dataset root so later discovery can avoid opening
Lance only to classify the tree ([RFC-0015](../../rfcs/0015-chronicle-manifest.md)).

## Import into a new Dataset

Expand Down
33 changes: 25 additions & 8 deletions docs/src/en/pchronicle/guides/serve.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ pchronicle serve
[--gateway-stream-markdown] [--gateway-debug]
[--catalog-config FILE]
[<[NAME=]DATASET> ...]
pchronicle serve catalog dataset add --catalog-config FILE NAME --uri URI [OPTIONS]
pchronicle serve catalog dataset remove --catalog-config FILE NAME...
pchronicle serve catalog dataset list --catalog-config FILE
pchronicle serve catalog issue --catalog-config FILE NAME
pchronicle serve catalog grant --catalog-config FILE NAME DATASET...
pchronicle serve catalog revoke --catalog-config FILE NAME DATASET...
Expand Down Expand Up @@ -49,17 +52,30 @@ still set mount names explicitly.
## Serve a path Directory

```bash
pchronicle serve catalog dataset add \
--catalog-config catalog.toml prod \
--uri s3://bucket/prod \
--endpoint http://127.0.0.1:9000 \
--region us-west-2 \
--access-key BACKEND_AK \
--secret-key BACKEND_SK
pchronicle serve catalog issue --catalog-config catalog.toml alice
pchronicle serve catalog grant --catalog-config catalog.toml alice prod evals
pchronicle serve --catalog-config catalog.toml --listen 127.0.0.1:8081
```

`catalog.toml` lists libraries (each a path) and users. `serve catalog issue`
writes a user with empty grants and prints the secret once on stdout; `grant` /
`revoke` change `datasets` without starting HTTP. Restart serve after editing
the file. The parent process does not open those paths itself. The Web UI sends
user access/secret keys as headers; queries run in a one-shot worker that
receives only that user's paths. From another terminal:
`catalog.toml` lists libraries (`[datasets.*]`, each a path or `s3://` URI) and
users. `serve catalog dataset add|remove|list` rewrites libraries without
starting HTTP. `serve catalog issue` writes a user with empty grants and prints
the secret once on stdout; `grant` / `revoke` change which library names that
user may open. Restart serve after editing the file.

`pchronicle serve --catalog-config` mounts **every** library in the file into
Warehouse (same as positional mounts). It also enables Directory ticket routes
for `catalog://` aliases. Do not combine `--catalog-config` with positional
Dataset mounts. Backend S3 endpoint, region, and keys from the file are applied
before stores open. The Web UI may send Directory user access/secret keys as
headers when you use catalog-authenticated flows. From another terminal:

```bash
pchronicle alias add team catalog://127.0.0.1:8081 --ak USER_AK --sk USER_SK
Expand All @@ -69,8 +85,9 @@ pchronicle query @team/prod --sql 'SELECT 1'
`@team` is a Directory locator, not a Dataset. `@team/prod` fetches a ticket and
opens the ticket `uri` (a path). All `s3://` libraries in one Directory file must
share the same endpoint, region, and backend keys. The listener remains
loopback-only. The design is specified in
[RFC-0013](../../rfcs/0013-pchronicle-warehouse-catalog.md).
loopback-only. Nested Dataset discovery may use `chronicle.manifest` sidecars
([RFC-0015](../../rfcs/0015-chronicle-manifest.md)). The Directory design is
specified in [RFC-0013](../../rfcs/0013-pchronicle-warehouse-catalog.md).

## Enable Control or Gateway integration

Expand Down
12 changes: 7 additions & 5 deletions docs/src/en/pchronicle/guides/ui.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,13 @@ untrusted or shared browser profile. Clearing this site's browser data also
clears the setting. Assistant is labeled **Read-only · selected run data** and
does not rewrite the Dataset.

When `pchronicle serve --catalog-config` is used, open **Keys** on the left rail
and enter the Directory user access key and secret key. Those values are stored in
`localStorage` and sent to this pChronicle server as `x-pchronicle-access-key`
and `x-pchronicle-secret-key` on data requests. They authorize which paths this
browser may open; they are not the object-store backend keys.
When `pchronicle serve --catalog-config` is used, every library in the ACL file
is already mounted for local browsing. Open **Keys** on the left rail if you
need Directory user access/secret headers for authenticated Directory flows.
Those values are stored in `localStorage` and sent to this pchronicle server as
`x-pchronicle-access-key` and `x-pchronicle-secret-key` on data requests. They
authorize which Directory paths this browser may open; they are not the
object-store backend keys.

## Troubleshooting

Expand Down
75 changes: 41 additions & 34 deletions docs/src/en/pchronicle/reference/cases-platform.md
Original file line number Diff line number Diff line change
@@ -1,54 +1,59 @@
# pChronicle 集群平台与 Catalog Server 场景
# pChronicle Directory and platform cases

本文覆盖平台化部署。Catalog 配置只管理用户、Dataset 和授权;Warehouse 的服务参数仍由 `pchronicle serve` 提供。
Platform-oriented Directory setup. The ACL file manages users, datasets
(libraries), and grants; Warehouse listen/Gateway options still come from
`pchronicle serve`.

## P01:从空配置创建 Catalog 用户
## P01: Issue a Directory user from an empty config

```bash
pchronicle serve catalog user create \
pchronicle serve catalog issue \
--catalog-config ./catalog.toml alice
```

如果文件不存在,命令创建配置文件、生成用户 AK/SK,并只在本次输出 secret。
If the file does not exist, the command creates it, writes a user with empty
grants, and prints the secret once on stdout.

## P02:登记 Dataset
## P02: Register a dataset library

```bash
pchronicle serve catalog dataset create \
pchronicle serve catalog dataset add \
--catalog-config ./catalog.toml \
prod s3://bucket/prod \
prod \
--uri s3://bucket/prod \
--endpoint http://127.0.0.1:9000 \
--region us-west-2 \
--ak BACKEND_AK \
--sk BACKEND_SK
--access-key BACKEND_AK \
--secret-key BACKEND_SK
```

该命令只登记 Dataset,不创建或删除后端数据。
This only registers the URI and backend credentials. It does not create or
delete object-store data. All `s3://` libraries in one file must share the same
endpoint, region, and backend keys.

## P03:授权用户
## P03: Grant libraries to a user

```bash
pchronicle serve catalog grant \
--catalog-config ./catalog.toml \
alice prod \
--permission read \
--permission query \
--permission analyze
alice prod
```

预期:配置中出现独立的 `[[grants]]` 记录。
Expected: a `[[grants]]` entry lists `prod` under that user. v1 grants are
library membership (not `--permission` flags).

## P04:启动 Catalog Server
## P04: Serve with catalog mounts

```bash
pchronicle serve \
--catalog-config ./catalog.toml \
--listen 127.0.0.1:8081
```

父进程负责用户认证、Dataset 列表和 ticket;查询数据面在授权 mounts 的 worker 中执行。
Every `[datasets.*]` entry is mounted into Warehouse. Directory ticket routes
remain available for `catalog://` aliases. Restart after editing the ACL file.

## P05:访问授权 Dataset
## P05: Open an authorized dataset via Directory alias

```bash
pchronicle alias add team catalog://127.0.0.1:8081 \
Expand All @@ -57,21 +62,21 @@ pchronicle query @team/prod \
--sql 'SELECT COUNT(*) AS runs FROM dataset.runs'
```

预期:授权用户可以查询 `prod`;未授权用户或未知 Dataset 返回相同的 404 资源错误。
Expected: an authorized user can query `prod`; unknown datasets fail closed.

## P06:撤销授权
## P06: Revoke a library grant

```bash
pchronicle serve catalog revoke \
--catalog-config ./catalog.toml \
alice prod --permission query
alice prod
```

预期:后续查询被拒绝,但 `read` 和其它仍保留的权限不受影响。
Expected: later `@team/prod` access is denied for that user.

## P07RustFS Warehouse 回归
## P07: RustFS Warehouse regression

准备 RustFS,并设置:
Prepare RustFS and set:

```bash
export PCHRONICLE_RUSTFS_ENDPOINT=http://127.0.0.1:9000
Expand All @@ -80,13 +85,15 @@ export PCHRONICLE_RUSTFS_SECRET_KEY=rustfsadmin
export PCHRONICLE_RUSTFS_BUCKET=pchronicle-cases
```

然后运行 RustFS 回归测试,验证 Dataset 写入、Catalog discovery、SQL 查询、Explorer 和 refresh 行为。
Then run the RustFS regression coverage for Dataset writes, Snapshot discovery
(including `chronicle.manifest` when present), SQL, Explorer, and refresh.

平台验收重点:
Platform checks:

- Catalog 文件可从空文件开始构建;
- 用户、Dataset 和 grants 修改是确定性的;
- Dataset 后端凭据只在授权 ticket 中使用;
- Worker 只收到当前用户被授权的 mounts;
- Catalog refresh 不影响已完成查询的 snapshot;
- RustFS 上的 Warehouse 行为与本地 Dataset 一致。
- ACL files can be built from empty;
- user, dataset, and grant edits are deterministic;
- backend object-store keys stay in the catalog file / ticket path, not in
`alias list` output;
- Warehouse mounts every registered library when serving `--catalog-config`;
- Snapshot refresh does not mutate an in-flight Snapshot;
- RustFS Warehouse behavior matches local Datasets for the covered paths.
Loading
Loading