Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

201 lines
8.1 KiB
Markdown

# Scratchpad — Extension Scheduler Host API
- Plan slug: `extension-scheduler-host-api`
- Created: `2026-01-02`
## What This Is
Rolling notes for implementing the scheduler host API capability. Captures discoveries, decisions, and implementation details.
## Decisions
- (2026-01-02) Use host API pattern (like `cap:http.fetch`, `cap:storage.kv`) rather than manifest-declared schedules — simpler conceptually, maximum flexibility for extension authors.
- (2026-01-02) Extensions reference endpoints by path (e.g., `/sync`) not UUID — server resolves path to `endpoint_id` using the installed version's endpoint table.
- (2026-01-02) Reuse existing `extensionScheduleActions.ts` logic — no duplication of validation, quotas, or job runner integration.
## Discoveries / Constraints
### Capability System
- (2026-01-02) Capabilities defined in `ee/server/src/lib/extensions/providers.ts`:
```typescript
export const KNOWN_PROVIDER_CAPABILITIES = [
'cap:context.read',
'cap:secrets.get',
'cap:http.fetch',
'cap:storage.kv',
'cap:log.emit',
'cap:ui.proxy',
'cap:user.read',
] as const;
```
- (2026-01-02) Default capabilities (always granted): `cap:context.read`, `cap:log.emit`, `cap:user.read`
- (2026-01-02) `isKnownCapability()` validates capability strings; `coerceProviders()` normalizes input
### Host Bindings Pattern
- (2026-01-02) SDK interface in `sdk/extension-runtime/src/index.ts`:
```typescript
export interface HostBindings {
context: { get(): Promise<ContextData> };
secrets: SecretsHost;
http: HttpHost;
storage: StorageHost;
logging: LoggingHost;
uiProxy: UiProxyHost;
}
```
- (2026-01-02) Extensions receive `HostBindings` as second argument to handler functions
- (2026-01-02) Runner implements these interfaces and communicates with host server
### Runner Execution Flow
- (2026-01-02) Runner backend defined in `ee/server/src/lib/extensions/runner/backend.ts`
- (2026-01-02) `RunnerExecutePayload` includes `providers?: unknown` array — this is how capabilities are communicated
- (2026-01-02) Runner receives `POST /v1/execute` with JSON payload including providers list
- (2026-01-02) Runner is responsible for implementing host bindings based on granted providers
### Existing Schedule Actions
- (2026-01-02) All CRUD in `ee/server/src/lib/actions/extensionScheduleActions.ts`:
- `listExtensionSchedules(extensionId)` — list schedules for an install
- `createExtensionSchedule(extensionId, input)` — create with validation
- `updateExtensionSchedule(extensionId, scheduleId, input)` — update with reschedule logic
- `deleteExtensionSchedule(extensionId, scheduleId)` — delete with job cancellation
- `runExtensionScheduleNow(extensionId, scheduleId)` — immediate trigger
- (2026-01-02) Validation includes: cron format, timezone, endpoint ownership, quotas (50 max, 5-min minimum)
- (2026-01-02) Uses `ensureExtensionPermission()` for auth — we need equivalent for host API context
### Key Implementation Gap
- (2026-01-02) Current actions use `getCurrentUser()` and `hasPermission()` — host API calls come from Runner, not user session
- (2026-01-02) Need to create internal versions that accept install context directly (tenant_id, install_id) rather than deriving from user session
- (2026-01-02) Could wrap existing actions or create parallel internal functions
## Commands / Runbooks
- Validate plan: `python3 ~/.codex/skills/alga-plan/scripts/validate_plan.py ee/docs/plans/2026-01-02-extension-scheduler-host-api`
## Links / References
- Capability definitions: `ee/server/src/lib/extensions/providers.ts`
- SDK host bindings: `sdk/extension-runtime/src/index.ts`
- Runner backend: `ee/server/src/lib/extensions/runner/backend.ts`
- Schedule actions: `ee/server/src/lib/actions/extensionScheduleActions.ts`
- Sample extension using host APIs: `sdk/samples/component/service-proxy-demo/src/handler.ts`
- Parent plan (scheduled tasks): `ee/docs/plans/2026-01-01-extension-scheduled-tasks/`
## Implementation Notes
### Endpoint Resolution
Extensions will reference endpoints by path:
```typescript
await host.scheduler.create({ endpoint: '/sync', cron: '0 * * * *' });
```
Server-side resolution:
```sql
SELECT id FROM extension_api_endpoint
WHERE version_id = $versionId
AND path = $path
AND method IN ('GET', 'POST');
```
### Internal API vs Action Reuse
Two approaches for server-side handler:
**Option A: Internal HTTP API**
- Create `/api/internal/extensions/scheduler/*` routes
- Runner calls these with auth token identifying install
- Routes delegate to modified action functions
**Option B: Direct action adaptation**
- Create `*ForInstall` variants of existing actions
- Accept `(tenantId, installId, ...)` instead of using `getCurrentUser()`
- Runner calls these via RPC mechanism
Recommendation: Start with Option A for clearer separation; consider B if performance is an issue.
### Context Passing to Runner
The Runner receives execution context:
```typescript
{
context: {
tenant_id: string,
registry_id: string,
install_id: string,
version_id: string,
content_hash: string,
// ...
},
providers: ['cap:scheduler.manage', ...],
// ...
}
```
Runner can use `install_id` and `tenant_id` from context to scope all scheduler operations.
## Open Questions (Resolved)
- How does Runner currently call back to host for `cap:storage.kv`?
- **Answer**: HTTP callback to internal API endpoint `/api/internal/ext-storage/install/[installId]`
- Runner uses `STORAGE_API_BASE_URL` and `RUNNER_STORAGE_API_TOKEN` env vars
- Auth via `x-runner-auth` header
- Should we expose `runNow()` via the host API, or is that admin-only?
- **Answer**: Admin-only. Extensions should not be able to bypass scheduling.
- Do we need a `getEndpoints()` API so extensions can discover their own schedulable endpoints?
- **Answer**: Yes! Added `getEndpoints()` to return all endpoints with `schedulable` flag.
## Implementation Summary (2026-01-02)
### Files Created/Modified
**Server-side:**
- `ee/server/src/lib/extensions/providers.ts` — Added `cap:scheduler.manage` to `KNOWN_PROVIDER_CAPABILITIES`
- `ee/server/src/lib/extensions/schedulerHostApi.ts` — New internal API for schedule operations
- `ee/server/src/app/api/internal/ext-scheduler/install/[installId]/route.ts` — Internal REST endpoint for Runner callbacks
**SDK:**
- `sdk/extension-runtime/src/index.ts` — Added `SchedulerHost` interface and all related types
**Runner (Rust):**
- `ee/runner/wit/extension-runner.wit` — Added scheduler types and interface
- `ee/runner/src/providers/mod.rs` — Added `CAP_SCHEDULER_MANAGE` constant
- `ee/runner/src/engine/host_api.rs` — Full scheduler host implementation with HTTP callbacks
**Sample Extension:**
- `sdk/samples/component/scheduler-demo/` — Full sample extension demonstrating:
- Self-configuration on `/api/setup` endpoint
- Listing schedules
- Deleting schedules
- Schedulable endpoints (`/api/status`, `/api/heartbeat`)
### Key Patterns Used
1. **HTTP Callback Pattern**: Runner uses HTTP POST to internal API (same as `cap:storage.kv`)
2. **Install Context**: Derived from install config lookup using `installId` in URL path
3. **Endpoint Resolution**: Extensions specify "METHOD /path", server resolves to `endpoint_id`
4. **Error Translation**: HTTP status codes mapped to WIT error enums
### Completed (2026-01-02)
All 84 features are now implemented:
- [x] F067: Expose metrics for scheduler API calls — Added OpenTelemetry metrics (counter, histogram, errors) to `schedulerHostApi.ts`
- [x] F071: Document scheduler host API in SDK documentation — Created comprehensive guide at `sdk/docs/guides/scheduler-host-api.md`
- [x] F080: Rate limiting on create/update operations via host API — Added in-memory sliding window rate limiter (10 ops/min) to internal API route
### Testing Notes
The Runner Rust code compiles successfully (`cargo check` passes).
Sample extension handler tests defined but require running in WASM context.
Full integration testing requires:
1. Install sample extension with `cap:scheduler.manage`
2. Call `/api/setup` endpoint
3. Verify schedules created in database and job runner