Jobs, Schemas, Router, Assets, Storage Paths, Site Auth, and Config Validation trait implementations
Scanned 9/3/2026
Install to Claude Code
npx -y skills add systempromptio/systemprompt-template --skill dev-ext-infrastructure --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Dev Ext Infrastructure?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/systempromptio-dev-ext-infrastructure)More formats (shields.io, HTML) on the badges page.
---
name: "Extension: Infrastructure"
description: "Jobs, Schemas, Router, Assets, Storage Paths, Site Auth, and Config Validation trait implementations"
---
# Extension: Infrastructure
Infrastructure traits provide background processing, database schemas, API routes, asset management, authentication, and config validation.
---
## 1. Jobs
Background tasks that run on schedule or on demand.
### Trait
```rust
#[async_trait]
pub trait Job: Send + Sync {
fn name(&self) -> &'static str;
fn schedule(&self) -> &'static str;
fn tags(&self) -> Vec<&'static str>;
async fn execute(&self, ctx: &dyn JobContext) -> Result<JobResult>;
fn enabled(&self) -> bool { true }
fn run_on_startup(&self) -> bool { false }
}
```
### Schedule Format
Cron syntax: `seconds minutes hours day_of_month month day_of_week`
| Example | Meaning |
|---------|---------|
| `0 0 * * * *` | Every hour |
| `0 0 3 * * *` | Daily at 3 AM |
| `0 */15 * * * *` | Every 15 minutes |
### Running Jobs Manually
```bash
systemprompt infra jobs run {job_name}
systemprompt infra jobs list
```
### Registration
```rust
impl Extension for MyExtension {
fn jobs(&self) -> Vec<Arc<dyn Job>> {
vec![Arc::new(MyJob)]
}
}
```
---
## 2. Schemas
Database table definitions embedded in extensions at compile time.
```rust
impl Extension for MyExtension {
fn schemas(&self) -> Vec<SchemaDefinition> {
vec![SchemaDefinition::inline("my_table", include_str!("../schema/my_table.sql"))]
}
fn migration_weight(&self) -> u32 { 100 }
fn dependencies(&self) -> Vec<&'static str> {
vec!["users"]
}
}
```
### Schema Conventions
| Convention | Rule |
|------------|------|
| Primary key | `id UUID PRIMARY KEY DEFAULT gen_random_uuid()` |
| Timestamps | Always `TIMESTAMPTZ`, never `TIMESTAMP` |
| Indexes | Create for frequently queried columns |
| Naming | `snake_case` for tables and columns |
| Schema file | `{crate}/schema/{table_name}.sql` |
---
## 3. Router
HTTP API endpoints provided by extensions.
```rust
impl Extension for MyExtension {
fn router(&self, ctx: &dyn ExtensionContext) -> Option<ExtensionRouter> {
let db = ctx.database().clone();
let router = axum::Router::new()
.route("/my-endpoint", axum::routing::get(handle_get))
.route("/my-endpoint", axum::routing::post(handle_post))
.with_state(db);
Some(ExtensionRouter::new("/api/ext/my-extension", router))
}
}
```
Extension routes should mount under `/api/ext/` to avoid colliding with core routes.
---
## 4. Assets
CSS, JavaScript, fonts, and images provided by extensions.
```rust
impl Extension for MyExtension {
fn declares_assets(&self) -> bool { true }
fn required_assets(&self, paths: &dyn AssetPaths) -> Vec<AssetDefinition> {
vec![
AssetDefinition::css("my-styles.css", include_bytes!("../assets/my-styles.css")),
]
}
}
```
### Asset Pipeline
```
storage/files/css/ <- CSS source
storage/files/js/ <- JS source
extensions/web/src/assets.rs <- Register assets
web/dist/ <- Output (generated by copy_extension_assets)
```
---
## 5. Storage Paths
Directories required by the extension under `storage/files/`.
```rust
impl Extension for MyExtension {
fn required_storage_paths(&self) -> Vec<String> {
vec!["documents".into(), "exports".into()]
}
}
```
---
## 6. Site Authentication
```rust
impl Extension for MyExtension {
fn site_auth(&self) -> Option<SiteAuthConfig> {
Some(SiteAuthConfig {
protected_paths: vec!["/admin".into()],
login_redirect: "/admin/login".into(),
exclude_paths: vec!["/admin/login".into()],
})
}
}
```
---
## 7. Config Validation
```rust
impl Extension for MyExtension {
fn config_prefix(&self) -> Option<&str> { Some("my_extension") }
fn validate_config(&self, config: &Value) -> Result<ValidationReport> {
let mut report = ValidationReport::new();
if config.get("api_key").is_none() {
report.add_error("my_extension.api_key is required");
}
Ok(report)
}
}
```
---
## 8. Rules
| Rule | Rationale |
|------|-----------|
| Jobs must return `JobResult` | Enables tracking in scheduler |
| Schemas use `include_str!()` | Compile-time embedding |
| Migration weight 100+ for extensions | Core uses 1-40 |
| Router endpoints under `/api/ext/` | Avoid collisions |
| All SQL uses `TIMESTAMPTZ` | Never `TIMESTAMP` |
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!