cufflink-cli 0.5.0

CLI for the Cufflink CRUD microservice platform — deploy, init, and manage services
cufflink-cli-0.5.0 is not a library.

Cufflink

Deploy CRUD microservices in seconds. Define your data model in Rust, run cufflink deploy, and get a full REST API with PostgreSQL persistence, automatic schema migrations, NATS events, and multi-tenant isolation.

Architecture

┌─────────────┐     ┌──────────────┐     ┌───────────┐
│  Cufflink   │────>│   Platform   │────>│ PostgreSQL│
│  CLI        │     │   (Axum)     │     │           │
└─────────────┘     └──────┬───────┘     └───────────┘
                           │
┌─────────────┐     ┌──────┴───────┐     ┌───────────┐
│  Web Admin  │────>│  Dynamic     │────>│   NATS    │
│  (Next.js)  │     │  CRUD Router │     │ JetStream │
└─────────────┘     └──────────────┘     └───────────┘

SDK#[derive(Table)] proc macros generate JSON manifests from Rust structs CLIcufflink deploy captures the manifest and POSTs it to the platform Platform — Applies schema migrations, registers CRUD routes, publishes events Web Admin — Dashboard for browsing services, data, schemas, and deployments

Quick Start

Prerequisites

  • Rust 1.70+
  • Docker & Docker Compose

1. Start the platform

docker compose up -d

This starts PostgreSQL, NATS, Keycloak, the Cufflink platform, and the web admin.

  • Platform API: http://localhost:8080
  • Web Admin: http://localhost:3000
  • NATS Monitoring: http://localhost:8222

2. Define a service

// examples/todo-service/src/main.rs
use cufflink::prelude::*;

#[derive(Table, Serialize, Deserialize, Clone)]
#[table(name = "todos")]
pub struct Todo {
    #[key] pub id: Uuid,
    pub title: String,
    pub completed: bool,
    #[timestamp] pub created_at: DateTime<Utc>,
    #[timestamp] pub updated_at: DateTime<Utc>,
}

cufflink::service! {
    name: "todo-service",
    tables: [Todo],
}

3. Deploy

cd examples/todo-service
cargo run -p cufflink -- deploy

4. Use the API

# Create
curl -X POST http://localhost:8080/svc/default/todo-service/todos \
  -H "Content-Type: application/json" \
  -d '{"title": "Buy groceries", "completed": false}'

# List (with filtering, sorting, search, pagination)
curl "http://localhost:8080/svc/default/todo-service/todos?sort=-created_at&per_page=10"

# Search
curl "http://localhost:8080/svc/default/todo-service/todos?search=groceries"

# Filter
curl --get http://localhost:8080/svc/default/todo-service/todos \
  --data-urlencode 'filter=[{"n":"completed","f":"=","v":"false"}]'

# Get by ID
curl http://localhost:8080/svc/default/todo-service/todos/{id}

# Update
curl -X PUT http://localhost:8080/svc/default/todo-service/todos/{id} \
  -H "Content-Type: application/json" \
  -d '{"completed": true}'

# Delete
curl -X DELETE http://localhost:8080/svc/default/todo-service/todos/{id}

SDK Reference

Table Attributes

Attribute Description
#[table(name = "...")] SQL table name (required)
#[key] Primary key (auto-generated UUID)
#[timestamp] Auto-generated timestamp (DEFAULT now())
#[default("value")] Default value
#[references("table.column")] Foreign key reference
#[on_delete("cascade")] FK delete action: cascade, set_null, restrict, no_action

Supported Types

Rust Type SQL Type
Uuid UUID
String TEXT
bool BOOLEAN
i32 INTEGER
i64 BIGINT
f32 REAL
f64 DOUBLE PRECISION
DateTime<Utc> TIMESTAMPTZ
NaiveDate DATE
Value JSONB
Option<T> Nullable version of T

Foreign Keys

#[derive(Table, Serialize, Deserialize, Clone)]
#[table(name = "comments")]
pub struct Comment {
    #[key] pub id: Uuid,
    #[references("posts.id")]
    #[on_delete("cascade")]
    pub post_id: Uuid,
    pub body: String,
}

CLI Reference

Command Description
cufflink init <name> Scaffold a new service project
cufflink deploy Deploy the service in the current directory
cufflink deploy --allow-destructive Allow DROP COLUMN / DROP TABLE
cufflink rollback Rollback to previous version
cufflink rollback --version N Rollback to specific version
cufflink status Show service status and endpoints
cufflink services List all deployed services
cufflink logs Stream service logs
cufflink openapi Print OpenAPI spec
cufflink openapi --output spec.json Save OpenAPI spec to file
cufflink generate-client Generate TypeScript client
cufflink login Authenticate with the platform

Configuration

Env Var Default Description
CUFFLINK_API_URL http://localhost:8080 Platform API URL
CUFFLINK_TENANT default Tenant slug
CUFFLINK_KEYCLOAK_URL http://localhost:8180 Keycloak URL
CUFFLINK_CLIENT_ID cufflink-cli Keycloak client ID

API Endpoints

Management API

Method Path Description
GET /health Health check
POST /api/auth/bootstrap Create first API key (no auth)
POST /api/auth/api-keys Create API key (requires auth)
GET /api/auth/me Get authenticated user info
GET /api/services List all services
GET /api/services/{id} Get service details
DELETE /api/services/{id} Delete a service
POST /api/services/deploy Deploy a service
GET /api/services/{id}/deployments Deployment history
POST /api/services/{id}/rollback Rollback deployment
GET /api/services/{id}/openapi.json Auto-generated OpenAPI spec
POST /api/services/{id}/wasm Upload WASM module
GET /api/services/{id}/wasm/status WASM runtime status

Dynamic CRUD Routes

Method Path Description
GET /svc/{tenant}/{service}/{table} List records
POST /svc/{tenant}/{service}/{table} Create record
GET /svc/{tenant}/{service}/{table}/{id} Get record
PUT /svc/{tenant}/{service}/{table}/{id} Update record
DELETE /svc/{tenant}/{service}/{table}/{id} Delete record

Query Parameters

Parameter Example Description
page ?page=2 Page number
per_page ?per_page=25 Items per page (max 200)
sort ?sort=-created_at,title Sort (prefix - for DESC)
search ?search=hello Full-text search across text columns
filter ?filter=[{"n":"status","f":"=","v":"active"}] JSON filter array
select ?select=id,title Column selection

Filter Operators

=, !=, >, <, >=, <=, LIKE, ILIKE, IN, IS NULL, IS NOT NULL

Authentication

Bootstrap (first-time setup)

curl -X POST http://localhost:8080/api/auth/bootstrap \
  -H "Content-Type: application/json" \
  -d '{"tenant_name": "My Org", "tenant_slug": "my-org"}'

Using API Keys

curl -H "Authorization: ApiKey ck_..." http://localhost:8080/api/auth/me

CLI Login

cufflink login

Project Structure

cufflink/
├── platform/          # Axum-based platform server
├── cli/               # CLI tool (cufflink binary)
├── sdk/               # User-facing SDK crate
├── sdk-macros/        # Proc macros (#[derive(Table)], service!{})
├── types/             # Shared types (ServiceManifest, etc.)
├── web/               # Next.js web admin dashboard
├── examples/
│   ├── todo-service/  # Simple single-table example
│   └── blog-service/  # Multi-table example with FK
├── docker-compose.yml
└── Dockerfile.platform

Development

# Build everything
cargo build --workspace

# Run tests
cargo test --workspace

# Run platform locally (requires Docker services)
docker compose up -d postgres nats keycloak
cargo run -p cufflink-platform

# Run web admin locally
cd web && npm install && npm run dev

License

MIT