DocHub
Standard environment, skills, permissions, and logging for every developer container

Container Setup & Standards

Principle

Every LXC container is set up identically from a base template. Same tools, same skills, same conventions. A developer can move between containers and know exactly how everything works.

Base Template

All containers were cloned from a template with:

Package Purpose
Node.js 22 Runtime for Claude Code and projects
Claude Code 2.1.x CLI agent
Docker 29.2.1 Containerized databases and services
tmux 3.4 Persistent sessions
git Version control
gh (GitHub CLI) GitHub operations
mosh Mobile-friendly SSH
build-essential Compilation tools
python3, pip Python projects
jq, curl, wget, rsync Utilities

Docker-in-LXC

All containers have Docker installed with full compose support. The LXC host config enables nesting:

lxc.include = /usr/share/lxc/config/nesting.conf
lxc.apparmor.profile = unconfined
lxc.mount.auto = proc:rw sys:rw cgroup:rw

Container User

All containers use the dev user (uid 1001). The dev user has:

  • Home directory: /home/dev
  • Password-less sudo access
  • SSH key-based authentication only
  • ~/bin in PATH (for custom scripts)

Account Assignment

Container Developer Account Plan
chasclaude Chas Account 1 Max
infoclaude Chas (secondary) Account 2 Max
seanclaude Sean Account 3 Max
jazclaude Jaz Account 4 Max
manager Supervisor Account 5 Pro

Claude Code Settings

Each container has ~/.claude/settings.json:

{
  "mcpServers": {
    "claude-net": {
      "command": "node",
      "args": ["/home/dev/.claude-net/mcp-server.js"]
    }
  },
  "hooks": {
    "Notification": [{
      "hooks": [{"type": "command", "command": "/home/dev/.claude/notify-sound.sh"}]
    }],
    "Stop": [{
      "hooks": [{"type": "command", "command": "/home/dev/.claude/notify-sound.sh"}]
    }]
  }
}

Standard Skills

Every container has these skills in ~/.claude/commands/:

Git Workflow Skills

Skill Purpose
/commit Smart git commit — analyzes changes, writes descriptive message, stages selectively, never amends
/push Push to remote with safety checks — sets up tracking, warns on conflicts, never force pushes
/pull Pull from remote with conflict awareness — offers stash if dirty
/status Quick project overview — branch, remote, uncommitted work, recent history

Session Management Skills

Skill Purpose
/end-day Full session wrap-up — updates memory, changelog, handover, DocHub, commits, pushes
/change-project Same as /end-day — use when switching between projects
/handover Same as /end-day — use when handing off to another developer

All three names (/end-day, /change-project, /handover) run the same workflow:

  1. Gathers git state (branch, status, log, remote)
  2. Asks developer for notes and context
  3. Updates Claude memory files with session patterns/decisions
  4. Updates CHANGELOG.md
  5. Writes HANDOVER.md (current state for next session)
  6. Checks DocHub coverage and updates documentation
  7. Commits everything (code + docs + memory)
  8. Pushes to GitHub

Documentation Skills

Skill Purpose
/onboard-repo Set up a new project — creates CLAUDE.md, GitHub repo, DocHub documentation
/dochub-report Scan codebase and generate coverage report — shows documented vs missing
/dochub-add Create new DocHub pages (Tier 1 overview + Tier 2 markdown + Tier 3 manifest)
/dochub-fix Fix all documentation gaps found by the latest report
/deploy-dochub Compile TypeScript and restart DocHub on production droplet

All skills are project-agnostic and work with any git repository.

tmux Auto-Attach with Session Logging

Each container has ~/.tmux-session.sh that runs on login:

#!/bin/bash
SESSION="main"
LOGFILE="/var/log/claude-sessions/$(date +%Y-%m-%d_%H%M%S).log"
if tmux has-session -t $SESSION 2>/dev/null; then
    tmux attach -t $SESSION
else
    tmux new-session -d -s $SESSION
    tmux pipe-pane -t $SESSION "cat >> $LOGFILE"
    tmux attach -t $SESSION
fi

This means:

  • Login always attaches to the persistent “main” tmux session
  • New sessions automatically start logging via pipe-pane
  • Logs go to /var/log/claude-sessions/ within each container
  • Disconnecting (Ctrl+B, D) or losing connection leaves the session running
  • Reconnecting picks up exactly where you left off

Claude Net MCP

Each container has Claude Net configured at ~/.claude-net/:

File Purpose
config.json Machine name, transport config
mcp-server.js MCP server (patched for localhost)

Transport uses the host’s SSH tunnel to cms-droplet:

  • sshHost: "local" — no SSH per request
  • localUrl: "http://10.0.3.1:3500" — hits host bridge → tunnel → hub
  • Latency: ~95ms (vs ~1.1s with per-request SSH)

Project CLAUDE.md Convention

Every project repo has a CLAUDE.md at the root. This is the AI’s instruction manual:

# Project Name — CLAUDE.md

## What This Project Is
One paragraph description.

## Tech Stack
Languages, frameworks, databases, key dependencies.

## How to Run
Commands to get the project running locally.

## Project Structure
Key directories and files explained.

## Current Work Status
What's been done, what's in progress, what's next.

## Conventions
Coding style, naming, branch naming, commit format.

## Key Decisions
Architecture decisions and WHY. Prevents reopening settled questions.

## Do NOT
Things the AI should never do in this project.

New Developer Onboarding

When a new developer joins:

  1. Clone LXC container from base template
  2. Assign static IP and port forwarding on host
  3. Add DNS A record for <name>.ipnoelp.io → 15.204.90.153
  4. Install Claude Code CLI, authenticate (claude login)
  5. Set up GitHub auth (gh auth login)
  6. Copy standard ~/.claude/settings.json and skills
  7. Configure Claude Net MCP client
  8. Set up SSH keys
  9. Clone project repos
  10. Install Docker with LXC nesting config
  11. Test: SSH → tmux → claude → /status

Container-Specific Overrides

While all containers follow the same base template, some have additional services:

chasclaude — Full Dev Environment

Fully provisioned with 8 project repos, Docker databases (CMS Postgres, WIT Postgres, Odoo), Gmail + Resend MCP servers, SSH keys to all production servers, and 9.1 GB of audio files. See the Architecture page for full details.

infoclaude — Production Projects

Hosts lifeonroatan and roatan-news with systemd services and cron jobs. nginx on the OVH5 host proxies news.ipnoelp.com traffic to this container. See the Architecture page for full details.

Permissions

All containers have Claude Code configured to auto-approve non-destructive operations. This eliminates prompt fatigue for routine commands.

Settings in ~/.claude/settings.json:

{
  "permissions": {
    "allow": [
      "Bash(*)",
      "Read(*)",
      "Edit(*)",
      "Write(*)",
      "Glob(*)",
      "Grep(*)",
      "WebFetch(*)",
      "WebSearch"
    ],
    "deny": []
  }
}

This is deployed on all 5 LXC containers, the laptop, and ser-8.

Claude will only prompt for confirmation on truly destructive actions (force push, rm -rf, etc.) or when it needs clarification on the direction of work.

Session Logging

All tmux sessions are logged to /var/log/claude-sessions/ for later review and analysis.

How It Works

The ~/.tmux-session.sh script runs on every login:

  1. If a tmux session exists: activates pipe-pane logging, then attaches
  2. If no session exists: creates one, activates pipe-pane logging, shows the container banner, then attaches

Every keystroke and output is captured to timestamped log files: /var/log/claude-sessions/YYYY-MM-DD_HHMMSS.log

Key Detail

Logging activates on every attach, not just new sessions. This ensures no session is ever unlogged, even after container restarts or reconnections.

Log Retention

Logs accumulate indefinitely. The manager instance can access logs from all containers via the host for review.

Login Banners (MOTD)

Each container has a distinctive banner in /etc/motd that displays on login and when a new tmux session starts:

  ┌──────────────────────────────────────────┐
  │  CHASCLAUDE  ·  Chas Primary Dev         │
  │  OVH5 LXC · chas.ipnoelp.io:2211        │
  │  Claude Net: chasclaude                  │
  └──────────────────────────────────────────┘

This prevents confusion when multiple terminal windows are open.