AI Decision System
Replayable, offline-verifiable records for AI-assisted go/no-go decisions: experiment promotions, typed model answers and human-approved actions.
翻譯準備中
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證A modern, type-safe HTTP framework for Rust heavily inspired by **Angular** and **NestJS**.
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
A modern, type-safe HTTP framework for Rust heavily inspired by Angular and NestJS.
Armature brings the elegant decorator syntax and powerful dependency injection from the TypeScript/JavaScript ecosystem to Rust, combining the developer experience of NestJS with Rust's performance and safety guarantees.檢視完整 README ↗A modern, type-safe HTTP framework for Rust heavily inspired by Angular and NestJS.
Armature brings the elegant decorator syntax and powerful dependency injection from the TypeScript/JavaScript ecosystem to Rust, combining the developer experience of NestJS with Rust's performance and safety guarantees.
QUERY method (safe, idempotent reads with a request body), including body-keyed response cachingInstall the Armature CLI for the best development experience:
# Install the CLI
cargo install armature-cli
# Create a new project
armature new my-api
# Navigate to your project
cd my-api
# Start the development server with hot reloading
armature dev
# Generate a controller
armature generate controller users
# Generate a service
armature generate service users
# Generate a complete resource (controller + service + module)
armature generate resource products --crud
# Generate middleware, guards, and more
armature generate middleware auth
armature generate guard admin
use armature::prelude::*;
use serde::{Deserialize, Serialize};
// Define your domain model
#[derive(Serialize, Deserialize)]
struct User {
id: u32,
name: String,
}
// Create an injectable service
#[injectable]
#[derive(Default, Clone)]
struct DatabaseService;
#[injectable]
#[derive(Default, Clone)]
struct UserService {
database: DatabaseService, // Automatically injected!
}
impl UserService {
fn get_users(&self) -> Vec<User> {
vec![User { id: 1, name: "Alice".to_string() }]
}
}
// Create a controller with automatic DI
#[controller("/users")]
#[derive(Default, Clone)]
struct UserController {
user_service: UserService, // Automatically injected!
}
impl UserController {
// Use injected service in methods
fn get_all(&self) -> Result<Json<Vec<User>>, Error> {
Ok(Json(self.user_service.get_users()))
}
fn get_one(&self, id: u32) -> Result<Json<User>, Error> {
// Use self.user_service throughout the controller
Ok(Json(User { id, name: "Alice".to_string() }))
}
}
// Define your application module
#[module(
providers: [DatabaseService, UserService],
controllers: [UserController]
)]
#[derive(Default)]
struct AppModule;
// Bootstrap your application - DI happens automatically!
#[tokio::main]
async fn main() {
let app = Application::create::<AppModule>();
app.listen(3000).await.unwrap();
}
The framework is organized into three main crates:
armature-coreCore runtime functionality including:
Provider, Controller, Module, RequestHandler)armature-macroProcedural macros for decorator syntax:
#[injectable] - Mark structs as injectable services#[controller("/path")] - Define controllers with base paths#[get], #[post], #[put], #[delete], #[patch] - HTTP route decorators#[module(...)] - Organize components into modules#[derive(Body)], #[derive(Param)], #[derive(Query)] - Request parameter extractionarmature-frameworkMain library that re-exports everything from core and macros. Add to your Cargo.toml:
[dependencies]
armature = { package = "armature-framework", version = "0.8" }
Or with specific features:
[dependencies]
armature = { package = "armature-framework", version = "0.8", features = ["auth", "cache", "validation"] }
#[injectable]Marks a struct as injectable, allowing it to be registered in the DI container:
#[injectable]
struct DatabaseService {
connection: DbConnection,
}
#[controller("/path")]Marks a struct as a controller with a base path:
#[controller("/api/users")]
struct UserController {
user_service: UserService,
}
HTTP method decorators for defining routes:
#[get("/")] // GET /api/users/
#[get("/:id")] // GET /api/users/:id
#[post("/")] // POST /api/users/
#[put("/:id")] // PUT /api/users/:id
#[delete("/:id")] // DELETE /api/users/:id
#[patch("/:id")] // PATCH /api/users/:id
#[options("/")] // OPTIONS /api/users/
#[head("/:id")] // HEAD /api/users/:id
#[query("/search")] // QUERY /api/users/search — safe query with a request body
#[query] implements the IETF QUERY method (draft-ietf-httpbis-safe-method-w-body):
a safe, idempotent read whose parameters travel in the request body rather than the URL.
It is cacheable — the response cache keys on the request body — so distinct queries to the
same path are distinct cache entries.
#[module(...)]Defines a module with providers, controllers, and imports:
#[module(
providers: [UserService, DatabaseService],
controllers: [UserController],
imports: [CommonModule],
exports: [UserService]
)]
struct UserModule;
The DI container uses Rust's type system for service resolution:
// Register a service
container.register(MyService::default());
// Resolve a service
let service = container.resolve::<MyService>()?;
Services are singletons by default and shared across the application.
Armature provides NestJS-inspired lifecycle hooks for managing service initialization and cleanup:
use armature::prelude::*;
use armature::lifecycle::{OnModuleInit, OnModuleDestroy, LifecycleResult};
use async_trait::async_trait;
#[injectable]
struct DatabaseService {
connected: Arc<RwLock<bool>>,
}
#[async_trait]
impl OnModuleInit for DatabaseService {
async fn on_module_init(&self) -> LifecycleResult {
println!("Connecting to database...");
*self.connected.write().await = true;
Ok(())
}
}
#[async_trait]
impl OnModuleDestroy for DatabaseService {
async fn on_module_destroy(&self) -> LifecycleResult {
println!("Closing database connection...");
*self.connected.write().await = false;
Ok(())
}
}
Available hooks:
OnModuleInit - Called after module initializationOnModuleDestroy - Called before module destructionOnApplicationBootstrap - Called after full application bootstrapOnApplicationShutdown - Called during graceful shutdownBeforeApplicationShutdown - Called before shutdown hooksSee the Lifecycle Hooks Guide for complete documentation.
The router supports:
/users/users/:id/users?page=1&limit=10Path parameters are extracted automatically:
#[get("/:id")]
async fn get_user(req: HttpRequest) -> Result<Json<User>, Error> {
let id = req.param("id").unwrap();
// ...
}
Comprehensive HTTP status code support with type-safe error handling:
use armature::{Error, HttpStatus};
// Type-safe status codes
let status = HttpStatus::NotFound;
assert_eq!(status.code(), 404);
assert_eq!(status.reason(), "Not Found");
// Structured errors for all 4xx/5xx codes
return Err(Error::NotFound("User not found".to_string()));
return Err(Error::TooManyRequests("Rate limit exceeded".to_string()));
return Err(Error::ServiceUnavailable("Database down".to_string()));
// Error helpers
let error = Error::Unauthorized("Invalid token".to_string());
assert_eq!(error.status_code(), 401);
assert!(error.is_client_error());
See the HTTP Status & Errors Guide for complete documentation.
Protect and transform your routes with Guards and Interceptors. Guards fail closed:
AuthenticationGuard and RolesGuard admit a request only when an authentication
layer has already verified the caller and attached a VerifiedIdentity (or
RequestRoles) extension. A bare Authorization: Bearer ... header is not enough.
The request pipeline follows NestJS order: global middleware → guards → handler. Register authentication as global middleware so the verified identity exists before any guard runs:
use armature::armature_auth::JwtAuthMiddleware;
use armature::armature_jwt::{JwtConfig, JwtManager};
use armature::prelude::*;
// HMAC secrets must be at least 32 bytes; weak or placeholder secrets are rejected.
let jwt = JwtManager::new(JwtConfig::new(std::env::var("JWT_SECRET")?)?)?;
let app = Application::create::<AppModule>()
.await
// Verifies the bearer token (access tokens only) and inserts
// `VerifiedIdentity` + `UserContext` before guards run.
.use_middleware(JwtAuthMiddleware::new(jwt));
// Built-in guards: AuthenticationGuard, RolesGuard, ApiKeyGuard
// `RolesGuard::new(vec![])` means "any authenticated caller", never "everyone".
See the Guards & Interceptors Guide for detailed documentation.
Recent releases tightened several defaults. The ones most likely to affect an upgrade:
// collapses and encoded unreserved
characters decode; ./.. segments, %2F, %5C, %00 and \ are refused with 400.
Module guards are bound to their module's routes, not to URL prefixes.BodyReadPolicy (30 s
inactivity, 1 KiB/s minimum rate, 408 otherwise), headers must arrive within 10 s,
TLS handshakes within 10 s, and accept loops cap concurrent connections
(PipelineConfig::max_connections, default 10 000).JwtConfig::new returns Result and rejects HMAC secrets under 32 bytes or
known placeholders; there is no Default. Refresh tokens are not accepted as bearer
tokens (JwtManager::verify_access).HttpRequest::client_address(trusted_proxy_depth), which is the socket peer unless
you trust a fixed number of proxies. X-Forwarded-For and X-Real-IP are not
trusted by default.Error::client_message).GraphQLConfig::development(endpoint) to opt in.Cookie builder (HttpResponse::set_cookie);
HttpResponse::cookie(name, value) no longer accepts attributes inside the value.Each crate's CHANGELOG.md lists the breaking changes and migrations.
The HttpRequest type provides:
json::<T>() - Parse body as JSONparam(name) - Get path parameterquery(name) - Get query parameterThe HttpResponse type provides:
with_json()ok(), created(), not_found(), etc.Use the Json<T> wrapper for automatic serialization:
#[get("/users")]
async fn get_users() -> Result<Json<Vec<User>>, Error> {
Ok(Json(vec![...]))
}
The framework provides a comprehensive Error type:
pub enum Error {
Http(String),
RouteNotFound(String),
MethodNotAllowed(String),
DependencyInjection(String),
ProviderNotFound(String),
Serialization(String),
Deserialization(String),
Validation(String),
Internal(String),
Io(std::io::Error),
}
Errors are automatically converted to HTTP responses with appropriate status codes.
Run the test suite:
cargo test
Build the library:
cargo build --lib
Run example applications:
# Full-featured example with CRUD operations
cargo run --example full_example
# Simple routing example
cargo run --example simple
# REST API example
cargo run --example rest_api
Test the endpoints (when running full_example):
# Health check
curl http://localhost:3000/health
# Get all users
curl http://localhost:3000/users
# Get user by ID
curl http://localhost:3000/users/1
# Create a user
curl -X POST http://localhost:3000/users \
-H 'Content-Type: application/json' \
-d '{"name":"Charlie","email":"[email protected]"}'
armature/
├── armature-core/ # Core runtime library
│ └── src/
│ ├── traits.rs # Core traits
│ ├── container.rs # DI container
│ ├── routing.rs # Router implementation
│ ├── http.rs # HTTP types
│ ├── error.rs # Error types
│ └── application.rs # Application bootstrap
├── armature-macro/ # Procedural macros
│ └── src/
│ ├── injectable.rs # #[injectable] macro
│ ├── controller.rs # #[controller] macro
│ ├── routes.rs # Route macros
│ ├── module.rs # #[module] macro
│ └── params.rs # Parameter extraction
├── armature-compression/ # HTTP response compression
│ └── src/
│ ├── algorithm.rs # Compression algorithms (gzip, brotli, zstd)
│ ├── config.rs # Configuration builder
│ └── middleware.rs # Compression middleware
├── src/
│ └── lib.rs # Main library (re-exports)
├── examples/ # Example applications
│ ├── full_example.rs # Complete CRUD example
│ ├── simple.rs # Basic routing
│ └── rest_api.rs # REST API demo
└── Cargo.toml # Workspace manifest
The Armature CLI provides powerful code generation and development tools:
cargo install armature-cli
| Command | Description |
|---|---|
armature new <name> | Create a new project from templates |
armature generate controller <name> | Generate a controller |
armature generate service <name> | Generate a service/provider |
armature generate module <name> | Generate a module |
armature generate middleware <name> | Generate middleware |
armature generate guard <name> | Generate a guard |
armature generate resource <name> | Generate controller + service + module |
armature dev | Start development server with file watching |
armature build | Build for production |
armature info | Display project information |
# Minimal API (default)
armature new my-api
# Full-featured API with auth, validation, Docker
armature new my-api --template full
# Microservice with queue worker
armature new my-api --template microservice
The armature dev command starts a development server with automatic rebuilding:
# Start with default settings (port 3000)
armature dev
# Custom port
armature dev --port 8080
# Uses cargo-watch if installed for better performance
#[use_middleware] decorator syntaxThis project is heavily inspired by:
We're grateful to these projects and their communities for showing what great developer experience looks like. Armature aims to bring these same patterns to the Rust ecosystem with the added benefits of memory safety and native performance.
MIT
🌐 Live Documentation Website: https://quinnjr.github.io/armature/
Comprehensive documentation is available in the docs/ directory:
Getting Started:
Core Features:
☁️ Cloud Providers:
Advanced:
And more guides covering testing, security, and deployment!
Armature provides first-class integrations with major cloud providers through dedicated crates:
| Crate | Provider | Key Services |
|---|---|---|
armature-aws | Amazon Web Services | S3, DynamoDB, SQS, SNS, SES, Lambda, KMS, Cognito |
armature-gcp | Google Cloud Platform | Cloud Storage, Pub/Sub, Firestore, Spanner, BigQuery |
armature-azure | Microsoft Azure | Blob Storage, Cosmos DB, Service Bus, Key Vault |
armature-redis | Redis | Connection pooling, Pub/Sub, Cluster support |
Features:
// Add only the services you need
[dependencies]
armature-aws = { version = "0.1", features = ["s3", "sqs"] }
armature-gcp = { version = "0.1", features = ["storage", "pubsub"] }
armature-redis = "0.1"
// Register cloud services in your DI container
#[module_impl]
impl CloudModule {
#[provider(singleton)]
async fn aws() -> Arc<AwsServices> {
let config = AwsConfig::from_env()
.enable_s3()
.enable_sqs()
.build();
AwsServices::new(config).await.unwrap()
}
#[provider(singleton)]
async fn redis() -> Arc<RedisService> {
Arc::new(RedisService::new(RedisConfig::from_env().build()).await.unwrap())
}
}
// Use in controllers via injection
#[controller("/files")]
impl FileController {
#[post("/upload")]
async fn upload(
&self,
#[inject] aws: Arc<AwsServices>,
body: Bytes,
) -> Result<Json<Response>, HttpError> {
let s3 = aws.s3()?;
s3.put_object()
.bucket("my-bucket")
.key("file.txt")
.body(body.into())
.send()
.await?;
Ok(Json(Response { success: true }))
}
}
📖 See the Cloud Providers Guide for complete documentation.
The documentation website is an Angular 21 application located in the web/ directory.
Local Development:
cd web
pnpm install
pnpm start
Then open http://localhost:4200 in your browser.
Building for Production:
cd web
pnpm run build
The built website will be in web/dist/web/browser/.
GitHub Pages Deployment:
The website automatically deploys to GitHub Pages when changes are merged to the main branch.
Contributions are welcome! Please read our Contributing Guide and feel free to submit a Pull Request.