Server Setup
This guide covers installing and configuring the Creddy server.
Installation
System Install (Recommended)
Install the binary to /usr/local/bin:
curl -fsSL https://get.creddy.dev/install.sh | sudo sh -s -- --to /usr/local/binInstall as a Daemon
Set up Creddy as a systemd service:
sudo creddy install --listen 0.0.0.0:8400This automatically:
- Copies the binary to
/usr/local/bin/creddy(if needed) - Creates
/var/lib/creddyfor data storage - Creates
/usr/local/lib/creddy/pluginsfor system plugins - Writes a hardened systemd unit file
- Enables and starts the service
Options:
| Flag | Default | Description |
|---|---|---|
--listen | 0.0.0.0:8400 | Address and port to listen on |
--data-dir | /var/lib/creddy | Data directory |
--agent-inactivity-days | 0 | Auto-unenroll inactive agents (0 = disabled) |
Managing the Service
systemctl status creddy # Check status
journalctl -u creddy -f # View logs
sudo systemctl restart creddy # Restart
sudo creddy uninstall # Remove serviceDiagnostics
Check your installation:
creddy whichOutput:
Binary:
Path: /usr/local/bin/creddy
Checksum: a1b2c3d4...
Config:
→ ✓ ~/.config/creddy/config.yaml
✗ /etc/creddy/config.yaml
Plugins:
Search order:
1. ✓ ~/.local/share/creddy/plugins (user)
2. ✓ /usr/local/lib/creddy/plugins (system)
Service:
Unit: /etc/systemd/system/creddy.service
ExecStart: /usr/local/bin/creddy server --listen 0.0.0.0:8400 ...
Status: activeCheck version and updates:
creddy version
creddy version --json # Machine-readable outputConfiguration
Data Directory
The server stores data in /var/lib/creddy (or --data-dir):
/var/lib/creddy/
├── creddy.db # SQLite database
└── keys/
└── signing.key # Ed25519 signing keyPlugin Directory
System plugins are stored in /usr/local/lib/creddy/plugins.
User plugins (from creddy plugin install) go to ~/.local/share/creddy/plugins.
The server searches both directories, so you can install plugins either way.
Running Without systemd
Foreground (Development)
creddy server --listen 127.0.0.1:8400Manual Systemd Setup
If you prefer to create the unit file yourself:
[Unit]
Description=Creddy credential server
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/creddy server --listen 0.0.0.0:8400 --db /var/lib/creddy/creddy.db
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/var/lib/creddy
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload
sudo systemctl enable creddy
sudo systemctl start creddyHealth Check
curl http://localhost:8400/healthReturns:
{
"status": "healthy",
"version": "v0.0.9"
}TLS & OIDC Setup
OIDC requires HTTPS. Choose the approach that fits your setup:
Tailscale Funnel (Easiest)
If you’re on Tailscale, Funnel gives you instant public HTTPS:
# Expose Creddy publicly with TLS
tailscale funnel 8400
# Start with OIDC
creddy server --oidc-issuer https://mybox.tail1234.ts.netYour issuer URL is your Tailscale machine name + .ts.net.
Caddy (Auto-TLS)
Caddy handles Let’s Encrypt automatically:
# Caddyfile
creddy.example.com {
reverse_proxy localhost:8400
}# Start Caddy
caddy run
# Start Creddy
creddy server --oidc-issuer https://creddy.example.comnginx + certbot
# Get cert
sudo certbot certonly --nginx -d creddy.example.com
# nginx.conf
server {
listen 443 ssl;
server_name creddy.example.com;
ssl_certificate /etc/letsencrypt/live/creddy.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/creddy.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8400;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}Cloud Load Balancer
AWS ALB, GCP LB, and Cloudflare all provide managed TLS:
- Point your LB to Creddy’s port
- Attach a managed certificate
- Use the LB’s hostname as your issuer
creddy server --oidc-issuer https://creddy.example.comInternal Only (No Federation)
If you don’t need AWS/GCP federation and all agents are on your network:
# Tailscale-only (no public exposure)
creddy server --oidc-issuer https://creddy.tail1234.ts.net
# Agents on tailnet can verify tokens
# External services (AWS) cannotNote: For AWS/GCP OIDC federation, your issuer URL must be publicly reachable so they can fetch
/.well-known/openid-configurationand/.well-known/jwks.json.
Remote Agent Provisioning
For automated workflows, set up a provisioner agent that can create other agents remotely.
1. Create the Provisioner (on server)
# SSH into your Creddy server
creddy agent create provisioner --can "admin:agents:*"Output:
{
"name": "provisioner",
"token": "ckr_abc123...",
"oidc": {
"client_id": "agent_f8e7d6c5b4a3",
"client_secret": "cks_xyz789..."
}
}Save the OIDC credentials securely (e.g., in your secrets manager).
2. Use from Your Automation
Copy the env vars from the CLI output, then use them to create task agents:
# From CLI output - already set:
# export CREDDY_URL=https://creddy-server.tail311b.ts.net
# export CREDDY_CLIENT_ID=agent_9a57c4e6db3a
# export CREDDY_CLIENT_SECRET=cks_0adf701f67231d2d08cd836c49574021
# Get admin token
CREDDY_TOKEN=$(curl -s -X POST $CREDDY_URL/oauth/token \
-d "grant_type=client_credentials" \
-d "client_id=$CREDDY_CLIENT_ID" \
-d "client_secret=$CREDDY_CLIENT_SECRET" \
| jq -r .access_token)
# Create a task agent with 4-hour TTL
AGENT_CREDS=$(curl -s -X POST $CREDDY_URL/v1/admin/agents \
-H "Authorization: Bearer $CREDDY_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "task-12345",
"scopes": ["github:owner/repo"],
"expires_in": "4h"
}')
# Extract credentials for the task agent
AGENT_CLIENT_ID=$(echo $AGENT_CREDS | jq -r '.oidc.client_id')
AGENT_CLIENT_SECRET=$(echo $AGENT_CREDS | jq -r '.oidc.client_secret')
AGENT_URL=$(echo $AGENT_CREDS | jq -r '.server_url')
# Pass to your agent process...3. Agent Workflow
The task agent:
- Authenticates with its OIDC credentials
- Requests backend credentials as needed
- Does its work
- Self-deletes when done (or TTL expires as safety net)
# Agent receives these env vars from provisioner:
# CREDDY_URL, CREDDY_CLIENT_ID, CREDDY_CLIENT_SECRET
# Agent gets its own token
CREDDY_TOKEN=$(curl -s -X POST $CREDDY_URL/oauth/token \
-d "grant_type=client_credentials" \
-d "client_id=$CREDDY_CLIENT_ID" \
-d "client_secret=$CREDDY_CLIENT_SECRET" \
| jq -r .access_token)
# Get GitHub credential
GITHUB_TOKEN=$(curl -s $CREDDY_URL/v1/credentials/github \
-H "Authorization: Bearer $CREDDY_TOKEN" \
| jq -r .token)
# ... do work ...
# Clean up
curl -X DELETE $CREDDY_URL/v1/self \
-H "Authorization: Bearer $CREDDY_TOKEN"Admin Scopes Reference
| Scope | Permission |
|---|---|
admin:agents:read | List agents |
admin:agents:write | Create/delete agents |
admin:agents:* | Both (recommended for provisioner) |
admin:* | Full admin access |
See Concepts → Admin Scopes for the complete list.
Security Considerations
- Use TLS — Required for OIDC, recommended always
- Restrict network access — Firewall the port, only allow trusted agents
- Enable agent inactivity cleanup —
--agent-inactivity-days 30removes stale enrollments - Keep plugins updated —
creddy plugin outdatedchecks for updates
Next Steps
- Client Authentication — How agents enroll and authenticate
- GitHub Integration — Configure GitHub App credentials
- Building Integrations — Create custom plugins