@enopax/resource-api
v1.0.0
Published
Script-based resource API framework for Enopax platform
Maintainers
Readme
Enopax Resource API
Bridge your infrastructure to the Enopax platform using bash scripts. No complex frameworks, no SDK dependencies—just simple scripts that provision, monitor, and manage resources.
Purpose
The Enopax Resource API lets you integrate any infrastructure with the platform without writing application code. Whether you manage Git repositories, databases, Kubernetes clusters, or custom infrastructure, this API turns your operational knowledge into platform-ready resources.
Core Philosophy: Scripts over code. Configuration over compilation. Simplicity over complexity.
Quick Start
# Install dependencies
pnpm install
# Start the server (runs with example providers)
pnpm dev
# In another terminal, test it
curl http://localhost:3001/providers/infoThe server starts with two example providers ready to explore:
- Example Provider (
/v1/resources) - Skeleton implementation with logging - Git Provider (
/v1/git) - Real Git repository management
How It Works
Platform → API Server → Bash Scripts → Your Infrastructure- Platform calls your API with resource requests
- API executes your bash scripts with JSON input
- Scripts manage actual infrastructure (Docker, cloud APIs, databases)
- Scripts return JSON with resource details
- Platform gets structured response to show users
Architecture
One API instance serves multiple providers, each handling one resource type:
Provider API (localhost:3001)
├── /v1/resources → scripts/example/
└── /v1/git → scripts/git-repo/Each provider is self-contained:
- Independent scripts folder
- Own configuration
- Separate endpoint
- Can be added/removed without downtime
Installation
pnpm installRunning
Development (with output)
pnpm devProduction (background)
# Start
pnpm start
# Stop
pnpm stopServer runs on http://localhost:3001
Configuration
config.json defines server settings, authentication, and providers:
{
"server": {
"port": 3001,
"host": "localhost"
},
"authentication": {
"enabled": false,
"apiKey": ""
},
"providers": [
{
"apiVersion": "v1",
"endpoint": "/resources",
"scriptsFolder": "./scripts/example"
},
{
"apiVersion": "v1",
"endpoint": "/git",
"scriptsFolder": "./scripts/git-repo"
}
]
}Enable Authentication (Production)
{
"authentication": {
"enabled": true,
"apiKey": "your-secure-api-key-here"
}
}Generate secure keys:
openssl rand -hex 32Clients include API key in requests:
curl -H "X-API-Key: your-key" http://localhost:3001/v1/resourcesNote: /providers/info is always public (no auth required).
See docs/authentication.md for details.
API Endpoints
Discovery (Public)
GET /providers/infoReturns all available providers with their metadata, schemas, and endpoints.
Resource Operations (Protected when auth enabled)
Each provider gets its own endpoint:
POST /{apiVersion}/{endpoint} # Create resource
GET /{apiVersion}/{endpoint}?org={org}&project={project} # List resources (requires org+project)
GET /{apiVersion}/{endpoint}/:id # Get status
GET /{apiVersion}/{endpoint}/:id/metrics # Get metrics
PUT /{apiVersion}/{endpoint}/:id # Update resource
DELETE /{apiVersion}/{endpoint}/:id # Delete resourceExamples:
POST /v1/resources→ Example provider (create)GET /v1/resources?org=ACME%20Corp&project=Web%20App→ Example provider (list)POST /v1/git→ Git provider (create)GET /v1/git?org=ACME%20Corp&project=Web%20App→ Git provider (list)
Security Note: List endpoint requires both org and project query parameters to prevent information leakage across organisations.
Usage Examples
Provision a Resource
curl -X POST http://localhost:3001/v1/resources \
-H "Content-Type: application/json" \
-d '{
"name": "my-resource",
"sshKeys": ["ssh-rsa AAAAB3..."],
"userId": "user_123",
"projectId": "my_project",
"projectName": "My Project",
"organizationId": "my_org",
"organisationName": "My Org"
}'Response:
{
"success": true,
"id": "my_org-my_project-my_resource-abc123de",
"name": "my-resource",
"status": "active",
"message": "Resource created successfully",
"access": "http://localhost:3001/my_org-my_project-my_resource-abc123de"
}ID Format: orgslug-projectslug-resourceslug-randomstring
- Hyphens separate components (org, project, resource name, random string)
- Underscores allowed within slugs
- Random string ensures uniqueness (8 characters)
Create a Git Repository
curl -X POST http://localhost:3001/v1/git \
-H "Content-Type: application/json" \
-d '{
"name": "backend-api",
"sshKeys": ["ssh-rsa AAAAB3..."],
"userId": "user_123",
"projectName": "Web App",
"organisationName": "My Org"
}'List Resources
# List example resources for a project
curl "http://localhost:3001/v1/resources?org=My%20Org&project=Web%20App"
# List git repositories for a project
curl "http://localhost:3001/v1/git?org=My%20Org&project=Web%20App"Important: Both org and project parameters are required for security.
Get Resource Status
curl http://localhost:3001/v1/git/my_org-web_app-backend_api-abc123deGet Metrics
curl http://localhost:3001/v1/git/my_org-web_app-backend_api-abc123de/metrics?period=7dUpdate Configuration
curl -X PUT http://localhost:3001/v1/git/my_org-web_app-backend_api-abc123de \
-H "Content-Type: application/json" \
-d '{
"configuration": {
"repositoryName": "backend-api-v2"
}
}'Delete Resource
curl -X DELETE http://localhost:3001/v1/git/my_org-web_app-backend_api-abc123deDirectory Structure
provider-api/
├── server.js # Express API server
├── config.json # Server, auth, and provider configuration
├── package.json # Dependencies
├── bin/
│ ├── start.sh # Start server in background
│ └── stop.sh # Stop background server
├── scripts/
│ ├── example/ # Skeleton provider (learning/demos)
│ │ ├── config.json # Provider and resource type definition
│ │ ├── provision.sh # Create resource
│ │ ├── status.sh # Get status
│ │ ├── metrics.sh # Get metrics
│ │ ├── update.sh # Update resource
│ │ └── deprovision.sh # Delete resource
│ └── git-repo/ # Git repository provider
│ ├── common.sh # Shared configuration
│ ├── config.json # Provider and resource type definition
│ └── *.sh scripts # Git operations
└── docs/
├── architecture.md # System architecture
├── concept.md # Core concepts and philosophy
├── authentication.md # Authentication guide
└── testing.md # Testing guideCreating Your Own Provider
1. Create Scripts Folder
mkdir -p scripts/my-provider
cd scripts/my-provider2. Define Provider Configuration
Create config.json:
{
"typeId": "my-resource-type",
"displayName": "My Resource Type",
"description": "What this resource provides",
"category": "DATABASE",
"providerId": "my-provider",
"providerName": "My Provider",
"version": "1.0.0",
"website": "https://example.com",
"supportEmail": "[email protected]",
"supportedRegions": ["local"],
"status": "operational",
"configSchema": {
"type": "object",
"properties": {
"instanceName": {
"type": "string",
"title": "Instance Name"
}
},
"required": ["instanceName"]
},
"estimatedProvisioningTime": 60
}3. Create Scripts
provision.sh - Create resource:
#!/bin/bash
set -e
# Load common configuration and helper functions
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/common.sh"
read -r INPUT
# Parse input from flat structure
NAME=$(echo "$INPUT" | jq -r '.name // empty')
ORG_NAME=$(echo "$INPUT" | jq -r '.organisationName // "default"')
PROJECT_NAME=$(echo "$INPUT" | jq -r '.projectName // "default"')
# Slugify names and generate ID using helper functions
ORG_SLUG=$(slugify "$ORG_NAME")
PROJECT_SLUG=$(slugify "$PROJECT_NAME")
NAME_SLUG=$(slugify "$NAME")
ID=$(generate_id "$ORG_SLUG" "$PROJECT_SLUG" "$NAME_SLUG")
# Create your resource here
# (call APIs, create Docker containers, etc.)
# Output JSON
cat <<EOF
{
"success": true,
"id": "$ID",
"name": "$NAME",
"status": "active",
"message": "Resource created successfully",
"access": "https://my-resource.example.com/$ID"
}
EOFCreate similar scripts for:
status.sh- Check resource statusmetrics.sh- Collect metricsupdate.sh- Update resourcedeprovision.sh- Delete resource
Make scripts executable:
chmod +x *.sh4. Register Provider
Add to config.json:
{
"providers": [
{
"apiVersion": "v1",
"endpoint": "/my-resource",
"scriptsFolder": "./scripts/my-provider"
}
]
}5. Test
# Restart server
pnpm stop && pnpm start
# Test your provider
curl -X POST http://localhost:3001/v1/my-resource \
-H "Content-Type: application/json" \
-d '{"resourceId":"test","typeId":"my-resource-type","configuration":{"instanceName":"test"}}'Script Interface
Input (stdin)
Scripts receive JSON via stdin (flat structure):
{
"name": "resource-name",
"sshKeys": ["ssh-rsa AAAAB3...", "ssh-rsa AAAAC4..."],
"userId": "user_123",
"projectId": "my_project",
"projectName": "My Project",
"organizationId": "my_org",
"organisationName": "My Org"
}Note: projectId and organizationId are slugified names (lowercase, underscores), not UUIDs.
Output (stdout)
Scripts output JSON to stdout:
Success (exit code 0):
{
"success": true,
"id": "my_org_my_project_resource_abc123",
"name": "resource-name",
"status": "active",
"message": "Resource created successfully",
"access": "https://..."
}Error (exit code non-zero):
{
"success": false,
"error": "Error message"
}Script Requirements
All scripts must:
- ✅ Read JSON from stdin
- ✅ Output valid JSON to stdout
- ✅ Exit with 0 on success, non-zero on error
- ✅ Be executable (
chmod +x) - ✅ Handle errors gracefully
Example Provider
The included example provider demonstrates the script interface with informative logging:
# Watch the execution flow
pnpm dev
# In another terminal
curl -X POST http://localhost:3001/v1/resources \
-H "Content-Type: application/json" \
-d '{"resourceId":"demo","typeId":"example-resource","configuration":{"resourceName":"demo","size":"small"}}'Server output shows script execution:
[provision.sh] Starting resource provisioning...
[provision.sh] Received input, parsing fields...
[provision.sh] Resource: demo (size: small)
[provision.sh] Generated orchestrator ID: example_1234_abc
[provision.sh] Creating resource...
[provision.sh] Resource created successfully!
[provision.sh] Returning JSON response...Perfect for learning the flow and building your own provider.
Git Repository Provider
The Git provider is a complete implementation that:
- Provisions - Creates bare Git repositories
- Status - Reports repository health and metadata
- Metrics - Returns storage size, commit count, branches
- Update - Modifies repository configuration
- Deprovision - Soft deletes with 30-day grace period
Features:
- Shared configuration via
common.sh - Organised by organisation/project/repository
- Configurable retention periods
- SSH access URLs
See scripts/git-repo/ for implementation details.
Testing
Quick Test
# Start server
pnpm dev
# Run test script (tests all endpoints)
./test.shManual Testing
# Test provider discovery
curl http://localhost:3001/providers/info | jq
# Test provisioning
curl -X POST http://localhost:3001/v1/resources \
-H "Content-Type: application/json" \
-d '{"resourceId":"test","typeId":"example-resource","configuration":{"resourceName":"test"}}' | jq
# Test status
curl http://localhost:3001/v1/resources/example_123 | jq
# Test metrics
curl http://localhost:3001/v1/resources/example_123/metrics | jq
# Test deletion
curl -X DELETE http://localhost:3001/v1/resources/example_123 | jqScript Testing
Test scripts independently without the API:
echo '{"name":"test","organizationId":"my_org","projectId":"my_project","sshKeys":[]}' | \
bash provider/git-repo/scripts/provision.shSee docs/testing.md for comprehensive testing guide.
Documentation
- Architecture - System design and multi-provider architecture
- Concept - Core concepts and design philosophy
- Authentication - API key authentication guide
- Testing - Testing strategies and examples
Platform Integration
The Enopax platform integrates with providers via this API:
- Discovery - Platform calls
/providers/infoto discover available resources - Provisioning - Platform calls provider endpoints to create resources
- Monitoring - Platform polls status endpoints for resource health
- Metrics - Platform collects usage data for billing and capacity planning
- Updates - Platform applies configuration changes via PUT requests
- Cleanup - Platform deprovisions resources when users delete them
The platform handles:
- User interface generation (from JSON schemas)
- Authentication and access control
- Credential encryption and storage
- Billing and usage tracking
- Multi-tenancy and organisation management
Common Configuration Pattern
Create common.sh to share settings and helper functions across scripts:
#!/bin/bash
# Get the directory of this script
COMMON_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Shared configuration
RESOURCES_BASE="$(cd "$COMMON_SCRIPT_DIR" && cd ../../../ && pwd)/data/resources"
mkdir -p "$RESOURCES_BASE"
# Slugify function: convert names to URL-safe identifiers
slugify() {
echo "$1" | \
sed 'y/äöüßÄÖÜ/aousAOU/' | \
sed 's/[^a-z0-9]/_/g' | \
tr '[:upper:]' '[:lower:]' | \
sed 's/__*/_/g' | \
sed 's/^_//; s/_$//'
}
# Parse ID components (ID format: orgslug-projectslug-resourceslug-randomstring)
get_org_from_id() {
local id=$1
IFS='-' read -ra parts <<< "$id"
[ ${#parts[@]} -eq 4 ] && echo "${parts[0]}"
}
get_project_from_id() {
local id=$1
IFS='-' read -ra parts <<< "$id"
[ ${#parts[@]} -eq 4 ] && echo "${parts[1]}"
}
get_name_from_id() {
local id=$1
IFS='-' read -ra parts <<< "$id"
[ ${#parts[@]} -eq 4 ] && echo "${parts[2]}"
}
# Generate unique ID
generate_id() {
local org_slug=$1
local project_slug=$2
local name_slug=$3
local random_str=$(echo $RANDOM | md5sum | head -c 8)
echo "${org_slug}-${project_slug}-${name_slug}-${random_str}"
}
# Construct resource path
get_resource_path() {
local org_slug=$1
local project_slug=$2
local id=$3
echo "$RESOURCES_BASE/$org_slug/$project_slug/$id.txt"
}
get_resource_path_by_id() {
local id=$1
local org_slug=$(get_org_from_id "$id")
local project_slug=$(get_project_from_id "$id")
get_resource_path "$org_slug" "$project_slug" "$id"
}Source in scripts:
#!/bin/bash
set -e
# Load common configuration and helper functions
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/common.sh"
# Use shared variables and functions
ORG_SLUG=$(slugify "$ORG_NAME")
PROJECT_SLUG=$(slugify "$PROJECT_NAME")
NAME_SLUG=$(slugify "$NAME")
ID=$(generate_id "$ORG_SLUG" "$PROJECT_SLUG" "$NAME_SLUG")
RESOURCE_FILE=$(get_resource_path "$ORG_SLUG" "$PROJECT_SLUG" "$ID")Benefits:
- Single source of truth for configuration
- Consistent ID format across all resources
- Helper functions reduce code duplication
- Easy to parse IDs for resource lookup
- Organised directory structure by org/project
See scripts/example/common.sh and scripts/git-repo/common.sh for full implementations.
Production Deployment
Enable Authentication
{
"authentication": {
"enabled": true,
"apiKey": "generated-secure-key"
}
}Use HTTPS
Run behind reverse proxy (Nginx, Caddy, Traefik):
server {
listen 443 ssl;
server_name provider.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:3001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}Process Management
Use systemd, PM2, or Docker:
systemd service:
[Unit]
Description=Enopax Provider API
After=network.target
[Service]
Type=simple
User=provider
WorkingDirectory=/opt/provider-api
ExecStart=/usr/bin/node server.js
Restart=always
[Install]
WantedBy=multi-user.targetMonitoring
- Check
/providers/infofor health - Monitor script execution times
- Track API response codes
- Log errors for debugging
Security Considerations
Production Checklist
- ✅ Enable authentication
- ✅ Use strong API keys
- ✅ Run behind HTTPS
- ✅ Validate all inputs
- ✅ Never log credentials
- ✅ Use environment variables for secrets
- ✅ Rotate API keys periodically
- ✅ Monitor for suspicious activity
- ✅ Implement rate limiting
- ✅ Keep dependencies updated
Script Security
- Validate all inputs
- Escape user-provided values
- Use secure temporary files
- Clean up sensitive data
- Handle errors safely
- Log operations for audit trail
Troubleshooting
Server won't start
- Check port 3001 is available
- Verify config.json is valid JSON
- Ensure scripts folders exist
- Check file permissions
Scripts fail
- Test scripts manually
- Verify scripts are executable
- Check JSON output is valid
- Review script logs (stderr)
Authentication errors
- Verify API key matches config
- Check X-API-Key header format
- Remember:
/providers/infois always public - Disable auth for testing
Script debugging
# Enable debug mode
bash -x scripts/example/provision.sh
# Test with verbose output
echo '{"test":"data"}' | bash -x scripts/example/provision.sh 2>&1Contributing
We welcome contributions! Whether it's:
- New provider examples
- Bug fixes
- Documentation improvements
- Feature suggestions
Please open issues or PRs on GitHub.
License
MIT
Built for developers, by developers. Simple, flexible, and powerful infrastructure integration.
