Back to skills
SKILL.md
Dev Ext Infrastructure
ASecurityJobs, Schemas, Router, Assets, Storage Paths, Site Auth, and Config Validation trait implementations
- 29 stars
- 0 votes
- 0 copies
- 2 views
- Added September 3, 2026
Works with
Security analysis
100/100npx -y skills add systempromptio/systemprompt-template --skill dev-ext-infrastructure --agent claude-codeAre you the author of Dev Ext Infrastructure?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/systempromptio-dev-ext-infrastructure)---
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` |
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.