mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
* add documentation for ai agents * docs: consolidate CLAUDE.md and CONTRIBUTING.md, add style guidelines - Refactor CLAUDE.md to reference CONTRIBUTING.md for build/test/format info - Expand CONTRIBUTING.md with comprehensive build targets, env vars, and testing - Add code style guidelines section with conventional commits - Standardize SDK prettier config to use single quotes (matching web) - Add project-level Claude Code settings to disable co-author attribution * style(sdk): apply prettier with single quotes Run prettier across sdk/base and sdk/package to apply the standardized quote style (single quotes matching web). * docs: add USER.md for per-developer TODO filtering - Add agents/USER.md to .gitignore (contains user identifier) - Document session startup flow in CLAUDE.md: - Create USER.md if missing, prompting for identifier - Filter TODOs by @username tags - Offer relevant TODOs on session start * docs: add i18n documentation task to agent TODOs * docs: document i18n ID patterns in core/ Add agents/i18n-patterns.md covering rust-i18n setup, translation file format, t!() macro usage, key naming conventions, and locale selection. Remove completed TODO item and add reference in CLAUDE.md. * chore: clarify that all builds work on any OS with Docker
227 lines
7.0 KiB
Markdown
227 lines
7.0 KiB
Markdown
# rpc-toolkit
|
|
|
|
StartOS uses [rpc-toolkit](https://github.com/Start9Labs/rpc-toolkit) for its JSON-RPC API. This document covers the patterns used in this codebase.
|
|
|
|
## Overview
|
|
|
|
The API is JSON-RPC (not REST). All endpoints are RPC methods organized in a hierarchical command structure.
|
|
|
|
## Handler Functions
|
|
|
|
There are four types of handler functions, chosen based on the function's characteristics:
|
|
|
|
### `from_fn_async` - Async handlers
|
|
For standard async functions. Most handlers use this.
|
|
|
|
```rust
|
|
pub async fn my_handler(ctx: RpcContext, params: MyParams) -> Result<MyResponse, Error> {
|
|
// Can use .await
|
|
}
|
|
|
|
from_fn_async(my_handler)
|
|
```
|
|
|
|
### `from_fn_async_local` - Non-thread-safe async handlers
|
|
For async functions that are not `Send` (cannot be safely moved between threads). Use when working with non-thread-safe types.
|
|
|
|
```rust
|
|
pub async fn cli_download(ctx: CliContext, params: Params) -> Result<(), Error> {
|
|
// Non-Send async operations
|
|
}
|
|
|
|
from_fn_async_local(cli_download)
|
|
```
|
|
|
|
### `from_fn_blocking` - Sync blocking handlers
|
|
For synchronous functions that perform blocking I/O or long computations.
|
|
|
|
```rust
|
|
pub fn query_dns(ctx: RpcContext, params: DnsParams) -> Result<DnsResponse, Error> {
|
|
// Blocking operations (file I/O, DNS lookup, etc.)
|
|
}
|
|
|
|
from_fn_blocking(query_dns)
|
|
```
|
|
|
|
### `from_fn` - Sync non-blocking handlers
|
|
For pure functions or quick synchronous operations with no I/O.
|
|
|
|
```rust
|
|
pub fn echo(ctx: RpcContext, params: EchoParams) -> Result<String, Error> {
|
|
Ok(params.message)
|
|
}
|
|
|
|
from_fn(echo)
|
|
```
|
|
|
|
## ParentHandler
|
|
|
|
Groups related RPC methods into a hierarchy:
|
|
|
|
```rust
|
|
use rpc_toolkit::{Context, HandlerExt, ParentHandler, from_fn_async};
|
|
|
|
pub fn my_api<C: Context>() -> ParentHandler<C> {
|
|
ParentHandler::new()
|
|
.subcommand("list", from_fn_async(list_handler).with_call_remote::<CliContext>())
|
|
.subcommand("create", from_fn_async(create_handler).with_call_remote::<CliContext>())
|
|
}
|
|
```
|
|
|
|
## Handler Extensions
|
|
|
|
Chain methods to configure handler behavior.
|
|
|
|
**Ordering rules:**
|
|
1. `with_about()` must come AFTER other CLI modifiers (`no_display()`, `with_custom_display_fn()`, etc.)
|
|
2. `with_call_remote()` must be the LAST adapter in the chain
|
|
|
|
| Method | Purpose |
|
|
|--------|---------|
|
|
| `.with_metadata("key", Value)` | Attach metadata for middleware |
|
|
| `.no_cli()` | RPC-only, not available via CLI |
|
|
| `.no_display()` | No CLI output |
|
|
| `.with_display_serializable()` | Default JSON/YAML output for CLI |
|
|
| `.with_custom_display_fn(\|_, res\| ...)` | Custom CLI output formatting |
|
|
| `.with_about("about.description")` | Add help text (i18n key) - **after CLI modifiers** |
|
|
| `.with_call_remote::<CliContext>()` | Enable CLI to call remotely - **must be last** |
|
|
|
|
### Correct ordering example:
|
|
```rust
|
|
from_fn_async(my_handler)
|
|
.with_metadata("sync_db", Value::Bool(true)) // metadata early
|
|
.no_display() // CLI modifier
|
|
.with_about("about.my-handler") // after CLI modifiers
|
|
.with_call_remote::<CliContext>() // always last
|
|
```
|
|
|
|
## Metadata by Middleware
|
|
|
|
Metadata tags are processed by different middleware. Group them logically:
|
|
|
|
### Auth Middleware (`middleware/auth/mod.rs`)
|
|
|
|
| Metadata | Default | Description |
|
|
|----------|---------|-------------|
|
|
| `authenticated` | `true` | Whether endpoint requires authentication. Set to `false` for public endpoints. |
|
|
|
|
### Session Auth Middleware (`middleware/auth/session.rs`)
|
|
|
|
| Metadata | Default | Description |
|
|
|----------|---------|-------------|
|
|
| `login` | `false` | Special handling for login endpoints (rate limiting, cookie setting) |
|
|
| `get_session` | `false` | Inject session ID into params as `__Auth_session` |
|
|
|
|
### Signature Auth Middleware (`middleware/auth/signature.rs`)
|
|
|
|
| Metadata | Default | Description |
|
|
|----------|---------|-------------|
|
|
| `get_signer` | `false` | Inject signer public key into params as `__Auth_signer` |
|
|
|
|
### Registry Auth (extends Signature Auth)
|
|
|
|
| Metadata | Default | Description |
|
|
|----------|---------|-------------|
|
|
| `admin` | `false` | Require admin privileges (signer must be in admin list) |
|
|
| `get_device_info` | `false` | Inject device info header for hardware filtering |
|
|
|
|
### Database Middleware (`middleware/db.rs`)
|
|
|
|
| Metadata | Default | Description |
|
|
|----------|---------|-------------|
|
|
| `sync_db` | `false` | Sync database after mutation, add `X-Patch-Sequence` header |
|
|
|
|
## Context Types
|
|
|
|
Different contexts for different execution environments:
|
|
|
|
- `RpcContext` - Web/RPC requests with full service access
|
|
- `CliContext` - CLI operations, calls remote RPC
|
|
- `InitContext` - During system initialization
|
|
- `DiagnosticContext` - Diagnostic/recovery mode
|
|
- `RegistryContext` - Registry daemon context
|
|
- `EffectContext` - Service effects context (container-to-host calls)
|
|
|
|
## Parameter Structs
|
|
|
|
Parameters use derive macros for JSON-RPC, CLI parsing, and TypeScript generation:
|
|
|
|
```rust
|
|
#[derive(Deserialize, Serialize, Parser, TS)]
|
|
#[serde(rename_all = "camelCase")] // JSON-RPC uses camelCase
|
|
#[command(rename_all = "kebab-case")] // CLI uses kebab-case
|
|
#[ts(export)] // Generate TypeScript types
|
|
pub struct MyParams {
|
|
pub package_id: PackageId,
|
|
}
|
|
```
|
|
|
|
### Middleware Injection
|
|
|
|
Auth middleware can inject values into params using special field names:
|
|
|
|
```rust
|
|
#[derive(Deserialize, Serialize, Parser, TS)]
|
|
pub struct MyParams {
|
|
#[ts(skip)]
|
|
#[serde(rename = "__Auth_session")] // Injected by session auth
|
|
session: InternedString,
|
|
|
|
#[ts(skip)]
|
|
#[serde(rename = "__Auth_signer")] // Injected by signature auth
|
|
signer: AnyVerifyingKey,
|
|
|
|
#[ts(skip)]
|
|
#[serde(rename = "__Auth_userAgent")] // Injected during login
|
|
user_agent: Option<String>,
|
|
}
|
|
```
|
|
|
|
## Common Patterns
|
|
|
|
### Adding a New RPC Endpoint
|
|
|
|
1. Define params struct with `Deserialize, Serialize, Parser, TS`
|
|
2. Choose handler type based on sync/async and thread-safety
|
|
3. Write handler function taking `(Context, Params) -> Result<Response, Error>`
|
|
4. Add to parent handler with appropriate extensions (display modifiers before `with_about`)
|
|
5. TypeScript types auto-generated via `make ts-bindings`
|
|
|
|
### Public (Unauthenticated) Endpoint
|
|
|
|
```rust
|
|
from_fn_async(get_info)
|
|
.with_metadata("authenticated", Value::Bool(false))
|
|
.with_display_serializable()
|
|
.with_about("about.get-info")
|
|
.with_call_remote::<CliContext>() // last
|
|
```
|
|
|
|
### Mutating Endpoint with DB Sync
|
|
|
|
```rust
|
|
from_fn_async(update_config)
|
|
.with_metadata("sync_db", Value::Bool(true))
|
|
.no_display()
|
|
.with_about("about.update-config")
|
|
.with_call_remote::<CliContext>() // last
|
|
```
|
|
|
|
### Session-Aware Endpoint
|
|
|
|
```rust
|
|
from_fn_async(logout)
|
|
.with_metadata("get_session", Value::Bool(true))
|
|
.no_display()
|
|
.with_about("about.logout")
|
|
.with_call_remote::<CliContext>() // last
|
|
```
|
|
|
|
## File Locations
|
|
|
|
- Handler definitions: Throughout `core/src/` modules
|
|
- Main API tree: `core/src/lib.rs` (`main_api()`, `server()`, `package()`)
|
|
- Auth middleware: `core/src/middleware/auth/`
|
|
- DB middleware: `core/src/middleware/db.rs`
|
|
- Context types: `core/src/context/`
|