Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions deploy/ansible/roles/anton-agent/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,50 @@
group: root
validate: "visudo -cf %s"

# Codex's inner sandbox (bwrap) needs unprivileged user namespaces:
# subuid/subgid ranges for the anton user, the userns_clone sysctl, and
# (on Ubuntu 24.04+) the AppArmor unprivileged-userns restriction lifted.
# Without these, every shell call fails with
# `bwrap: setting up uid map: Permission denied`.

- name: Grant subordinate UID range to anton user
lineinfile:
path: /etc/subuid
regexp: "^{{ anton_user }}:"
line: "{{ anton_user }}:100000:65536"
create: true
owner: root
group: root
mode: "0644"

- name: Grant subordinate GID range to anton user
lineinfile:
path: /etc/subgid
regexp: "^{{ anton_user }}:"
line: "{{ anton_user }}:100000:65536"
create: true
owner: root
group: root
mode: "0644"

- name: Enable unprivileged user namespaces (persistent)
sysctl:
name: kernel.unprivileged_userns_clone
value: "1"
state: present
sysctl_file: /etc/sysctl.d/99-anton-userns.conf
reload: true
ignore_errors: true # kernel may not expose this knob; safe to skip

- name: Lift AppArmor unprivileged-userns restriction (Ubuntu 24.04+)
sysctl:
name: kernel.apparmor_restrict_unprivileged_userns
value: "0"
state: present
sysctl_file: /etc/sysctl.d/99-anton-userns.conf
reload: true
ignore_errors: true # absent on pre-24.04 kernels

- name: Ensure home directory is traversable (for Caddy file serving)
file:
path: "/home/{{ anton_user }}"
Expand Down
19 changes: 19 additions & 0 deletions infra-providers/huddle/cloud-init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,25 @@ echo "anton ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/anton
chmod 0440 /etc/sudoers.d/anton
log "USER: anton system user ready (with sudo)"

# Codex's inner sandbox (bwrap) needs unprivileged user namespaces. System
# users created via `useradd --system` don't get subuid/subgid ranges by
# default, so add them explicitly and enable the kernel/AppArmor knobs.
# Without this: `bwrap: setting up uid map: Permission denied`.
if ! grep -q '^anton:' /etc/subuid 2>/dev/null; then
echo 'anton:100000:65536' >> /etc/subuid
fi
if ! grep -q '^anton:' /etc/subgid 2>/dev/null; then
echo 'anton:100000:65536' >> /etc/subgid
fi
mkdir -p /etc/sysctl.d
cat > /etc/sysctl.d/99-anton-userns.conf <<'SYSCTL'
kernel.unprivileged_userns_clone = 1
kernel.apparmor_restrict_unprivileged_userns = 0
SYSCTL
# Apply now; either knob may be absent depending on kernel — don't fail init.
sysctl -p /etc/sysctl.d/99-anton-userns.conf 2>/dev/null || true
log "USER: anton subuid/subgid + userns sysctls configured"

# ─────────────────────────────────────────────────────────────────
# 5. Download latest agent binary from manifest
# ─────────────────────────────────────────────────────────────────
Expand Down
24 changes: 22 additions & 2 deletions packages/agent-config/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,20 @@ export interface AgentConfig {

sessions?: {
ttlDays: number // auto-cleanup after N days, default 7
/**
* Behavior when the WebSocket client disconnects mid-turn.
* 'attached' (default): cancel the active turn on disconnect.
* 'detached': keep the turn running in the background;
* events buffer to the mirror and replay on
* reconnect. See specs/features/DETACHED_TURNS.md.
*/
disconnectMode?: 'attached' | 'detached'
/**
* Hard wall-clock budget for a detached turn that runs without a
* connected client. Prevents zombie turns from burning tokens
* forever if the user never comes back. Default 10 minutes.
*/
detachedTurnMaxMs?: number
}

compaction?: {
Expand Down Expand Up @@ -574,7 +588,9 @@ export function loadConfig(): AgentConfig {
skills: parsed.skills ?? defaults.skills ?? [],
connectors: parsed.connectors ?? defaults.connectors ?? [],
workspace: parsed.workspace ?? defaults.workspace,
sessions: parsed.sessions ?? defaults.sessions,
// Shallow-merge so configs that set only ttlDays still inherit the
// new disconnectMode / detachedTurnMaxMs defaults.
sessions: { ...defaults.sessions, ...(parsed.sessions ?? {}) },
}

// Apply CLI/env overrides (these take precedence over config file)
Expand Down Expand Up @@ -647,7 +663,11 @@ function createDefaultConfig(): AgentConfig {
skills: [],
connectors: [],
workspace: { root: DEFAULT_WORKSPACE_ROOT },
sessions: { ttlDays: 7 },
sessions: {
ttlDays: 7,
disconnectMode: 'attached',
detachedTurnMaxMs: 10 * 60 * 1000,
},
}
}

Expand Down
Loading