Skip to main content
ClaudeWave
Skill17.8k estrellas del repoactualizado 1mo ago

infisical-sync-skill

The infisical-sync-skill provides API integration knowledge for Infisical, a secrets management platform. It documents Universal Auth authentication flows, endpoints for listing and managing secrets across environments and projects, error handling patterns, and curl command examples. Use this skill when building integrations that synchronize secrets between Infisical vaults and external systems, requiring secure machine-to-machine communication and secrets lifecycle management.

Instalar en Claude Code
Copiar
git clone --depth 1 https://github.com/RightNow-AI/openfang /tmp/infisical-sync-skill && cp -r /tmp/infisical-sync-skill/crates/openfang-hands/bundled/infisical-sync ~/.claude/skills/infisical-sync-skill
Después abre una sesión nueva de Claude Code; el skill carga automáticamente.

SKILL.md

# Infisical Sync Expert Knowledge

## 1. Infisical API Reference

### Base URL
All requests go to `$INFISICAL_URL`. This is the self-hosted instance base URL, e.g. `https://infisical.example.com`.

### Authentication — Universal Auth
Infisical uses Machine Identities with Universal Auth for agent-to-agent communication.

**Endpoint**: `POST /api/v1/auth/universal-auth/login`

**Request**:
```json
{
  "clientId": "<INFISICAL_CLIENT_ID>",
  "clientSecret": "<INFISICAL_CLIENT_SECRET>"
}
```

**Response** (success):
```json
{
  "accessToken": "eyJ...",
  "expiresIn": 7200,
  "accessTokenMaxTTL": 43200,
  "tokenType": "Bearer"
}
```

**curl example**:
```bash
RESPONSE=$(curl -s -X POST "$INFISICAL_URL/api/v1/auth/universal-auth/login" \
  -H "Content-Type: application/json" \
  -d "{\"clientId\":\"$INFISICAL_CLIENT_ID\",\"clientSecret\":\"$INFISICAL_CLIENT_SECRET\"}")

ACCESS_TOKEN=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['accessToken'])")
```

Token lifetime: `expiresIn` seconds (usually 7200 = 2 hours). Re-authenticate when expired.

---

### List Secrets
**Endpoint**: `GET /api/v4/secrets`

**Query parameters**:
| Param | Required | Description |
|-------|----------|-------------|
| `projectId` | Yes | Infisical project ID |
| `environment` | Yes | Environment slug (e.g. `prod`, `staging`, `dev`) |
| `secretPath` | No | Path prefix, default `/` |
| `includeImports` | No | Include imported secrets, default `false` |
| `recursive` | No | Include secrets in sub-paths, default `false` |

**curl example**:
```bash
curl -s -X GET \
  "$INFISICAL_URL/api/v4/secrets?projectId=$PROJECT_ID&environment=$ENVIRONMENT&secretPath=/" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

**Response shape**:
```json
{
  "secrets": [
    {
      "id": "uuid",
      "version": 1,
      "secretKey": "DATABASE_URL",
      "secretValue": "postgres://...",
      "secretComment": "",
      "environment": "prod",
      "workspace": "uuid"
    }
  ],
  "imports": []
}
```

Parse with:
```bash
echo "$RESPONSE" | python3 -c "
import sys, json
data = json.load(sys.stdin)
for s in data.get('secrets', []):
    print(s['secretKey'])
"
```

---

### Create or Update a Secret

The API does **not** provide a single upsert endpoint. `POST` creates only (returns 409 if the secret already exists); `PATCH` updates only (returns 404 if missing). Use the create-then-update pattern:

**Step 1 — Try to create (POST)**  
**Endpoint**: `POST /api/v4/secrets/{secretName}`

**Request body**:
```json
{
  "projectId": "<PROJECT_ID>",
  "environment": "<ENV>",
  "secretValue": "<VALUE>",
  "secretPath": "/"
}
```

```bash
HTTP_STATUS=$(curl -s -o /tmp/infisical_response.json -w "%{http_code}" \
  -X POST "$INFISICAL_URL/api/v4/secrets/$SECRET_NAME" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"projectId\":\"$PROJECT_ID\",\"environment\":\"$ENVIRONMENT\",\"secretValue\":\"$SECRET_VALUE\",\"secretPath\":\"/\"}")
```

Returns `201` on success.

**Step 2 — If 409, update via PATCH**  
**Endpoint**: `PATCH /api/v4/secrets/{secretName}`

```bash
if [ "$HTTP_STATUS" = "409" ]; then
  HTTP_STATUS=$(curl -s -o /tmp/infisical_response.json -w "%{http_code}" \
    -X PATCH "$INFISICAL_URL/api/v4/secrets/$SECRET_NAME" \
    -H "Authorization: Bearer $ACCESS_TOKEN" \
    -H "Content-Type: application/json" \
    -d "{\"projectId\":\"$PROJECT_ID\",\"environment\":\"$ENVIRONMENT\",\"secretValue\":\"$SECRET_VALUE\",\"secretPath\":\"/\"}")
fi
```

Returns `200` on success. Any other status code is an error.

**Important**: URL-encode the secret name if it contains special characters.

---

### Delete a Secret
**Endpoint**: `DELETE /api/v4/secrets/{secretName}`

**Query parameters**: `projectId`, `environment`, `secretPath` (default `/`)

**curl example**:
```bash
curl -s -X DELETE \
  "$INFISICAL_URL/api/v4/secrets/$SECRET_NAME?projectId=$PROJECT_ID&environment=$ENVIRONMENT&secretPath=/" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

---

### List Accessible Projects (Workspaces)
**Endpoint**: `GET /api/v1/workspace`

```bash
curl -s -X GET "$INFISICAL_URL/api/v1/workspace" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

Response: `{ "workspaces": [{ "id": "uuid", "name": "...", "environments": [...] }] }`

---

## 2. HTTP Error Codes

| Code | Meaning | Action |
|------|---------|--------|
| 200/201 | Success | Continue |
| 400 | Bad Request | Log the response body — likely malformed JSON or missing field |
| 401 | Unauthorized | Re-authenticate; token may have expired |
| 403 | Forbidden | Machine identity lacks permissions — check Infisical Access Control |
| 404 | Not Found | Secret or project doesn't exist |
| 429 | Rate Limited | Wait 60 seconds, retry once |
| 500/503 | Server Error | Log + retry once after 30 seconds; notify if still failing |

Always check HTTP status before trusting response body:
```bash
HTTP_STATUS=$(curl -s -o /tmp/infisical_response.json -w "%{http_code}" ...)
if [ "$HTTP_STATUS" != "200" ] && [ "$HTTP_STATUS" != "201" ]; then
  # handle error
fi
RESPONSE=$(cat /tmp/infisical_response.json)
```

---

## 3. Sync State File Format

Stored at `infisical_sync_state.json`:
```json
{
  "last_sync": "2025-01-15T10:30:00Z",
  "project_ids": ["uuid1", "uuid2"],
  "environment": "prod",
  "secrets": {
    "DATABASE_URL": {
      "hash": "sha256_of_key_plus_value",
      "version": 3,
      "last_synced": "2025-01-15T10:30:00Z"
    }
  },
  "error_count": 0,
  "push_count": 12,
  "pull_count": 47
}
```

**Hash computation** (to detect changes without storing values):
```bash
echo -n "DATABASE_URL:postgres://..." | sha256sum | awk '{print $1}'
```

Or with Python:
```python
import hashlib
h = hashlib.sha256(f"{key}:{value}".encode()).hexdigest()
```

---

## 4. Vault Operations Reference

The local vault provides encrypted key-value storage. All secrets synced from Infisical go here.

| Operation | Description |
|-----------|-------------|
| `vault_se