---
title: "Access-control lists"
description: "Manage command, server and file access-control lists in Alpacon. Check signed-in access, Cloud restrictions, bulk requests, token limits and API errors."
---

> Full documentation index: https://docs.alpacax.com/llms.txt

# Access-control lists

**Endpoint**: `/api/security/`

Manage the command, server and file access-control lists (ACLs) that restrict tokens. You must be signed in through the console or CLI. No personal API token (`alpat-`) or service token (`alpst-`) can call these routes: both receive `403 permission_denied`, even with `command_acl`, `server_acl` or `file_acl` scopes.

On Alpacon Cloud, set command rules under **Pre-approved commands** on the token's page in the web console. Managing this list requires **Command ACL admins** access, separate from your workspace role; being a Superuser does not grant it. If the card says it is managed by Command ACL admins, ask one of them to add you. Choose **Connect** if the console asks you to connect your browser. The command ACL rows returned by this API do not decide which token commands run. Every command ACL write, including bulk requests, returns `405 command_acl_moved`. Server and file ACL writes remain available.

## Access and targets

You can see ACLs for your own personal tokens and for service tokens you can manage. Admins can see all service-token ACLs, including those belonging to deleted applications. Other callers can manage service tokens only for applications they may bind tokens to, and only when the token's existing server ACLs are within their visible servers. Server ACL lists omit deleted servers.

Every ACL belongs to exactly one token. Set either `token` or `service_token`. For writes, a personal token must be your own enabled token; a service token must be approved, enabled and manageable by you. A pending or disabled service-token target returns `400 does_not_exist` on `service_token`. Server ACL bulk deletion also accepts disabled tokens. To change token kind, clear the other field in the same request.

A service-token ACL must match the token's scopes: command ACLs require a `command` scope, file ACLs require an `uploaded_file` or `downloaded_file` scope, and server ACLs require a resource that accepts server restrictions. A server ACL must also remain within the application's server scope. Personal-token ACLs do not have these two service-token checks.

## Routes

Use `command-acl`, `server-acl` or `file-acl` for `{resource}` below.

| Method | Route | Response |
|--------|-------|----------|
| `GET` | `/api/security/{resource}/` | `200`, paginated ACLs |
| `POST` | `/api/security/{resource}/` | `201`, created ACL |
| `GET` | `/api/security/{resource}/{id}/` | `200`, ACL |
| `PUT`, `PATCH` | `/api/security/{resource}/{id}/` | `200`, updated ACL |
| `DELETE` | `/api/security/{resource}/{id}/` | `204`, no body |
| `POST` | `/api/security/{resource}/bulk/` | `201`, array of matching ACLs |
| `POST` | `/api/security/{resource}/bulk/delete/` | `200`, `{"deleted": 2}` |
| `POST` | `/api/security/command-acl/bulk/update/` | `200`, array of updated ACLs |
| `POST` | `/api/security/file-acl/bulk/update/` | `200`, array of updated ACLs |

There is no server ACL bulk-update route. Duplicate items in a bulk-create request do not create duplicate ACLs. Bulk delete and update ignore IDs outside your visible ACLs.

## Request and response fields

All ACLs return `id` (UUID), `token`, `service_token` and the read-only `token_name`, plus the fields for their type. Send token IDs, not token secrets.

### Command ACL

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `token` | UUID | One token field | Your personal API token ID |
| `service_token` | UUID | One token field | Service token ID |
| `command` | string | Yes | One command pattern, up to 255 characters; no operators, newlines or multiple commands |
| `username` | string | No | Up to 128 characters; default empty. Empty means the personal-token owner, or any user for a service token; `*` means any user |
| `groupname` | string | No | Up to 128 characters; default empty. Empty or `*` means any group |

Username and group values otherwise match exactly. Newline, carriage-return and null characters are refused. A command is matched after shell tokenization: a final word `*` matches one or more further arguments, while other `*` characters match within one word. A quoted `*` matches nothing. Nothing may follow the final literal. To permit `sudo command`, the pattern must include the `sudo ` prefix.

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "token": null,
  "service_token": "3f1c2a9e-7b6d-4e1f-9a2b-5c8d7e6f4a31",
  "token_name": "ci-deploy",
  "command": "df -h",
  "username": "deploy",
  "groupname": ""
}
```

### Server ACL

Send `token` or `service_token`, plus the required `server` UUID. The server must be visible to you. On read, `server` is an object with `id`, `name`, `os`, `platform`, `is_connected` and `is_deleted`.

```json
{
  "service_token": "3f1c2a9e-7b6d-4e1f-9a2b-5c8d7e6f4a31",
  "server": "7e3984de-49ab-4cc6-bcdf-21fbd35858b8"
}
```

### File ACL

Send `token` or `service_token`, plus these fields:

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `path` | string | Yes | Up to 512 characters; `*` matches any characters. Control characters are refused; `..` segments are normalized while a trailing wildcard is preserved |
| `action` | string | Yes | `upload`, `download` or `*` for both |
| `username` | string | No | Same matching rules as command ACLs; default empty |
| `groupname` | string | No | Same matching rules as command ACLs; default empty |

```json
{
  "service_token": "3f1c2a9e-7b6d-4e1f-9a2b-5c8d7e6f4a31",
  "path": "/srv/releases/*",
  "action": "upload",
  "username": "deploy",
  "groupname": ""
}
```

## Bulk requests

| Resource and action | Body |
|---------------------|------|
| Command `bulk/` | `token` or `service_token`, and `items` containing 1–100 objects with `command`, optional `username` and optional `groupname` |
| File `bulk/` | `token` or `service_token`, and `items` containing 1–100 objects with `path`, `action`, optional `username` and optional `groupname` |
| Server `bulk/` | `token` or `service_token`, and `servers` containing 1–100 UUIDs |
| Server `bulk/delete/` | `token` or `service_token`, and `servers` containing 1–100 UUIDs |
| Command or file `bulk/delete/` | `ids` containing 1–100 ACL UUIDs |
| Command `bulk/update/` | `ids` containing 1–100 ACL UUIDs, and at least one of `username`, `groupname` |
| File `bulk/update/` | `ids` containing 1–100 ACL UUIDs, and at least one of `username`, `groupname`, `action` |

Bulk creation returns matching rows, including existing matches. For server ACLs, the result contains the token's ACL rows for the submitted servers. Field limits match single creation.

For example, create server ACLs with this body:

```http
POST /api/security/server-acl/bulk/
```

```json
{
  "service_token": "3f1c2a9e-7b6d-4e1f-9a2b-5c8d7e6f4a31",
  "servers": ["7e3984de-49ab-4cc6-bcdf-21fbd35858b8"]
}
```

## List filters and pagination

| Parameter | Description |
|-----------|-------------|
| `token` | Filter by personal token UUID |
| `api_token` | Alias for `token` |
| `service_token` | Filter by service token UUID |
| `search` | Search personal and service token names |
| `page` | Integer page number, starting at 1 |
| `page_size` | Default 15, maximum 100; larger values are clamped to 100 |

```json
{
  "count": 0,
  "current": 1,
  "next": null,
  "previous": null,
  "last": 1,
  "results": []
}
```

The array contains ACL objects. `next` and `previous` are integer page numbers or `null`; follow `page=next` until `next` is `null`.

## Errors

| Status | Code | Meaning |
|--------|------|---------|
| `400` | `invalid_input` | Both or neither token fields, invalid control characters, or bulk update without a field to change |
| `400` | `acl_command_not_allowed` | Command pattern contains operators, a newline or multiple commands |
| `400` | `acl_exists` | Duplicate ACL: same token and command/user/group, server, or path/user/group/action |
| `400` | `does_not_exist` | Service-token target is pending, disabled or unavailable to you; reported on `service_token` |
| `400` | `acl_service_token_forbidden` | You cannot bind tokens to any application; reported on `service_token` |
| `400` | `acl_type_not_allowed_for_token` | The token has no scope that permits this ACL type |
| `400` | `acl_server_outside_application_scope` | A server lies outside the application's server scope |
| `401` | `auth_token_missing` | Authentication is missing |
| `403` | `permission_denied` | A personal API token or service token was used instead of a signed-in session |
| `403` | `workspace_suspended` | Write attempted in a suspended workspace |
| `404` | `api_not_found` | ACL does not exist or is outside your visible ACLs |
| `405` | `command_acl_moved` | Command ACL write on Alpacon Cloud |

There is no plan gate on these ACL routes. See the [API reference](/reference/api/) for the error envelope and [Service tokens](/reference/api/service-tokens/) for token management.

Source: https://docs.alpacax.com/reference/api/acl/
