# Background Charm Service
## Overview
The background-charm-service polls registered charms and triggers their
`bgUpdater` handlers server-side. This enables scheduled/background tasks in
charms without requiring the browser to be open.
**Key concepts:**
- Polls registered charms every 60 seconds (default)
- Sends `{}` to the charm's `bgUpdater` Stream on each poll
- The `bgUpdater` handler executes server-side, not in browser
---
## Running Locally (For Testing bgUpdater)
Use this when you're developing a charm with `bgUpdater` and want to test
server-side execution.
### Prerequisites
1. Local dev servers running (see `docs/common/LOCAL_DEV_SERVERS.md`)
2. Binaries built: `deno task build-binaries` (from labs root)
### Setup Steps
```bash
# 1. From labs root, ensure dev servers are running
./scripts/restart-local-dev.sh
# 2. Set up admin charm (one-time, grants service access to system space)
cd packages/background-charm-service
OPERATOR_PASS="implicit trust" API_URL="http://localhost:8000" deno task add-admin-charm
# 3. Start the background service (from labs root)
cd ..
OPERATOR_PASS="implicit trust" API_URL="http://localhost:8000" ./dist/bg-charm-service
```
### Registering Charms for Background Updates
Charms must be registered before they receive polling:
```bash
# Register via curl
curl -X POST http://localhost:8000/api/integrations/bg \
-H "Content-Type: application/json" \
-d '{
"charmId": "baedrei...",
"space": "did:key:z6Mk...",
"integration": "my-test"
}'
```
Or add `` to your charm's UI.
**Getting space DID from space name:**
```
Space DID = Identity.fromPassphrase("common user").derive(spaceName).did()
```
### Verifying It Works
Watch the service output for:
```
Successfully executed charm did:key:.../baedrei...
```
### Troubleshooting
| Issue | Solution |
| --------------------------------------------- | -------------------------------------------- |
| `CompilerError: no exported member 'pattern'` | Rebuild binaries: `deno task build-binaries` |
| `AuthorizationError` on system space | Run `add-admin-charm` step |
| Charm not being polled | Verify registration via curl |
---
## Developing the Service
Use this section when working on the background-charm-service code itself.
### Commands
| Command | Purpose |
| ------------------------------- | ----------------------- |
| `deno task start` | Run service from source |
| `deno task test` | Run tests |
| `deno test src/path/to/test.ts` | Run specific test |
| `deno task check` | Check typings |
| `deno fmt` | Format code |
| `deno lint` | Lint code |
### Integration-Specific Commands
| Command | Purpose |
| ---------------------------- | -------------------------------------- |
| `deno task gmail:kv` | Run with Gmail integration |
| `deno task gmail` | Run with Gmail integration (legacy) |
| `deno task initialize` | Initialize integration cells |
| `deno task initialize:gmail` | Initialize Gmail integration |
| `deno task initialize:gcal` | Initialize Google Calendar integration |
---
## Adding a New Integration
Create a new file in `src/integrations/` named after your integration (e.g.,
`myservice.ts`):
```typescript
import { Charm } from "@commontools/charm";
import { Cell } from "@commontools/runner";
import type { DID } from "@commontools/identity";
import { Integration, IntegrationCellConfig } from "../types.ts";
import { log } from "../utils.ts";
export class MyServiceIntegration implements Integration {
id = "myservice"; // Used for --integration flag
name = "My Service Integration";
async initialize(): Promise {
// Initialization logic
}
getIntegrationConfig(): IntegrationCellConfig {
return {
id: this.id,
name: this.name,
spaceId: "system",
cellId: "myservice-integration-charms",
fetchCharms: () => this.fetchMyServiceCharms(),
isValidIntegrationCharm: (charm) => this.isValidCharm(charm),
};
}
private async fetchMyServiceCharms(): Promise<
{ space: DID; charmId: string }[]
> {
// Implementation
return [];
}
private isValidCharm(charm: Cell): boolean {
// Validation logic
return true;
}
}
export default new MyServiceIntegration();
```
Then add a shortcut task to `deno.json`:
```json
"myservice": "deno run -A src/cli.ts --integration=myservice"
```
---
## Code Style
- **Formatting**: 2 spaces, semicolons required, double quotes, ~80 char lines
- **Naming**: PascalCase classes, camelCase functions, UPPER_SNAKE_CASE
constants
- **Types**: Prefer interfaces, export types explicitly, avoid `any`
- **Errors**: Try/catch with context (charm IDs), typed error classes
- **Testing**: Descriptive test names, mock external dependencies
Run `deno fmt` for auto-formatting.