Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/pixlcore/xyops/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Secrets are encrypted “vaults” for sensitive configuration such as API keys, auth tokens, passwords, and similar credentials. Each secret contains one or more named variables (key/value pairs). xyOps stores the variable data encrypted at rest and only decrypts it in memory when needed at runtime. Secrets can be assigned to:
  • Events: Jobs launched by events receive secret variables as environment variables
  • Categories: All events in selected categories receive the secrets
  • Plugins: Jobs using the plugin receive the secrets
  • Web Hooks: Hooks access secrets through template expansion via {{ secrets.VAR_NAME }}

Data Model

Secret metadata stored in plaintext:
  • id - Unique identifier
  • title - Display name
  • enabled - Active/inactive toggle
  • icon - Optional icon identifier
  • notes - Description (plaintext only)
  • names - List of variable names (not values)
  • Assignment lists: events, categories, plugins, web_hooks
Variable values stored in separate encrypted record:
  • Never exposed in list APIs
  • Only decrypted by administrators explicitly
  • Each access is logged
Variable values are always strings (delivered via environment variables). For binary data, Base64-encode first.

Encryption

xyOps uses authenticated encryption to protect secret values at rest:
// From source code: lib/secret.js
const SEC_ALGO = 'aes-256-gcm';
const SEC_SCRYPT_OPTS = { N: 16384, r: 8, p: 1 };

// Encryption implementation
const plaintext = JSON.stringify(secret);
const salt = crypto.randomBytes(16); // per-record KDF salt
const key = crypto.scryptSync(passphrase, salt, 32, SEC_SCRYPT_OPTS);
const iv = crypto.randomBytes(12); // 96-bit GCM nonce

const cipher = crypto.createCipheriv(SEC_ALGO, key, iv);
if (aad) cipher.setAAD(Buffer.from(String(aad)));

Encryption Details

1

Algorithm

AES-256-GCM for confidentiality and integrity
AES-256-GCM combines Advanced Encryption Standard with a 256-bit key and Galois/Counter Mode (GCM) to provide both data confidentiality and authentication.
2

Key Derivation

scrypt with parameters:
  • N=16384 (CPU/memory cost)
  • r=8 (block size)
  • p=1 (parallelization)
  • Per-record random 16-byte salt
3

Nonce & AAD

  • Nonce/IV: Per-record random 12-byte IV
  • AAD: Secret ID bound as Additional Authenticated Data to prevent record swapping
4

Storage

Encrypted blob includes:
  • alg - Algorithm identifier
  • salt - KDF salt
  • iv - Initialization vector
  • tag - Authentication tag
  • ct - Ciphertext
The encryption key is derived from config.secret_key. Keep this value strong and private in production.

Assigning Access

Secrets control where they may be used through assignment:
{
  "id": "zmeejkeb8nu",
  "title": "Dev Database",
  "enabled": true,
  "names": ["DB_HOST", "DB_USER", "DB_PASS"],
  "events": ["emeekm2ablu"],
  "categories": ["prod"],
  "plugins": ["shell"],
  "web_hooks": ["slack_notify"]
}
Grant to selected events - their jobs receive the variables:
// From source code: lib/secret.js
// First the event
Tools.mergeHashInto(env, this.getSecretsForType('events', job.event));
Grant to all events in selected categories:
// Now the category
Tools.mergeHashInto(env, this.getSecretsForType('categories', job.category));
Grant to selected plugins when they run jobs, actions, or triggers:
// Finally the plugin
Tools.mergeHashInto(env, this.getSecretsForType('plugins', job.plugin));
Grant to selected web hooks for template expansion:
{{!-- In webhook templates --}}
Authorization: Bearer {{ secrets.API_TOKEN }}

Merge Precedence

If multiple secrets define the same variable name, the final value is determined by merge order:
1. Event
2. Workflow sub-event (if applicable and different)
3. Category
4. Plugin (merged last, so plugin wins on conflicts)
Web hooks have no merging; each referenced secret’s variables are expanded independently in templates.

Runtime Delivery

Secret variables injected into job’s process environment as NAME=value pairs:
#!/bin/bash
# Secrets available as environment variables
echo "Connecting to $DB_HOST as $DB_USER"
mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASS" -e "SHOW DATABASES;"
Variables follow POSIX naming rules: letters, digits, underscores (start with letter or underscore).
Secrets available via template expansion:
{
  "url": "https://api.service.com/notify",
  "headers": {
    "Authorization": "Bearer {{ secrets.API_TOKEN }}",
    "X-Custom-Key": "{{ secrets.CUSTOM_KEY }}"
  },
  "body": {
    "message": "Job completed",
    "api_key": "{{ secrets.SERVICE_KEY }}"
  }
}
  • Encrypted data remains at rest until needed
  • xyOps decrypts into memory at exact moment required
  • Values used and never persisted in plaintext
  • Memory cleared after use

Auditing and Logging

xyOps records both routine and user-initiated access to secrets.

Routine Runtime Use

Logged “quietly” to dedicated Secret.log file:
// From source code: lib/secret.js
// Always log secret access (use log file for routine use)
this.logSecret(1, `Using secret ${secret.id} (${secret.title}) for ${type}: ${id}`, 
  { secret, type, id });
Log entry format:
[1763675628.397][2025-11-20 13:53:48][joemax.lan][62614][Secret][debug][1]
[Using secret zmeejkeb8nu (Dev Database) for events: emeekm2ablu]
[{"secret":{"id":"zmeejkeb8nu","title":"Dev Database","enabled":true,...}}]
Entry includes:
  • Epoch timestamp and formatted date/time
  • Server hostname and PID
  • Textual description
  • Full secret metadata JSON (no values)
  • Access type (event, category, plugin, or web hook)

Administrator Decryption

Logged “loudly” in Activity Log when admin decrypts through UI or API:
{
  "action": "secret_access",
  "username": "admin",
  "description": "Dev Database",
  "epoch": 1763675687,
  "id": "ami7yyuct2y",
  "useragent": "Safari 26.1.0 / macOS",
  "ip": "127.0.0.1",
  "secret": {
    "id": "zmeejkeb8nu",
    "title": "Dev Database"
  },
  "keywords": ["zmeejkeb8nu", "admin", "127.0.0.1"]
}
Create, update, and delete operations are also logged in the Activity Log.

Using Secrets in the UI

The Secrets admin page requires administrator privileges.
1

Create Secret

Define title, optional icon/notes, assignments, and variables:
  • Values encrypted on save
  • Only variable names stored in plaintext
  • ID auto-generated if not provided
2

Edit Metadata

Update title, icon, notes, and assignment lists without touching encrypted data:
  • No decryption required
  • Changes saved immediately
3

View/Edit Values

Values not loaded by default:
  • Click to view/decrypt (requires admin role)
  • Confirmation dialog displayed
  • Access logged to Activity Log
  • Saving re-encrypts and stores new payload
4

Enable/Disable

Toggle availability without deleting data:
  • Disabled secrets not injected into jobs
  • Useful for temporary suspension
5

Delete

Permanently removes metadata and encrypted payload:
  • Action is logged
  • Cannot be undone

API Usage

Key secret API endpoints:
# Get all secrets (metadata only, no values)
curl -X GET https://your.xyops.example.com/api/app/get_secrets/v1 \
  -H "X-API-Key: YOUR_API_KEY"

Best Practices

Keep titles/notes non-sensitive:
Do not include secret values or hints in title, notes, or key names - they are stored in plaintext.
Good:
  • Title: “Production Database”
  • Names: DB_HOST, DB_USER, DB_PASS
Bad:
  • Title: “DB password is hunter2”
  • Names: PROD_DB_ADMIN_PASSWORD
Use clear, uppercase names with underscores:
# Good
DB_HOST=prod-db.example.com
API_TOKEN=abc123xyz
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE

# Avoid
dbhost=...
apiToken=...
aws-access-key-id=...
Avoid variable name collisions across assigned secrets.
Base64-encode binary payloads before storing:
# Encode certificate
CERT_DATA=$(base64 -w 0 certificate.pem)

# Store in secret
curl -X POST .../create_secret/v1 \
  -d '{"fields": [{"name": "TLS_CERT", "value": "'$CERT_DATA'"}]}'

# Decode in job
echo "$TLS_CERT" | base64 -d > /tmp/cert.pem
Base64 increases size by ~33%. Keep this in mind for large payloads.
POSIX systems enforce limits on total argv+environment size:
  • Linux: Often ≥2 MB
  • macOS: Commonly ~256 KB
  • Check with: getconf ARG_MAX
Recommendations:
  • Keep each value under a few kilobytes
  • For large data, use files or Buckets and pass references
  • Monitor total environment size across all secrets
Prefer placing secrets in headers or body, not URLs:
// Good: Header
{"headers": {"Authorization": "Bearer {{ secrets.API_TOKEN }}"}}

// Good: Body
{"body": {"api_key": "{{ secrets.API_KEY }}"}}

// Bad: URL (logged in access logs)
{"url": "https://api.example.com/notify?token={{ secrets.API_TOKEN }}"}
Avoid logging expanded templates that would reveal secret values.
Ensure scripts don’t echo environment variables to logs:
#!/bin/bash

# Bad: Exposes secrets in logs
env
echo "Password: $DB_PASS"

# Good: Use secrets without logging
mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASS" -e "SELECT 1" 2>&1 | \
  sed 's/password.*/password: [REDACTED]/i'
Scrub or redact as needed.

Secret Key Rotation

The config.secret_key is used to derive encryption keys for all secrets. To rotate:
1

Backup

Create a full backup of your xyOps database before proceeding.
2

Stop xyOps

Stop the xyOps server to prevent secrets from being accessed during rotation.
3

Update Configuration

Change secret_key in your configuration file to a new strong value.
4

Re-encrypt Secrets

Use the provided migration tool to re-encrypt all secrets with the new key.
Consult the xyOps documentation for the specific re-encryption procedure for your version.
5

Restart

Start xyOps and verify secrets are accessible.

API Reference

  • get_secrets - List all secrets (metadata only)
  • get_secret - Get single secret metadata
  • decrypt_secret - Decrypt and retrieve values (admin only, logged)
  • create_secret - Create new secret with encrypted values
  • update_secret - Update secret metadata or values
  • delete_secret - Permanently delete secret

Required Privileges

  • Administrator role: Required to decrypt secrets, create, update, or delete
  • Valid session or API Key: Required to list secrets and view metadata
Jobs automatically receive secrets based on assignments - no special privileges required.

Build docs developers (and LLMs) love