Files
kiddo/CLAUDE.md
stephan 107cdabe8d feat: implement update backend API and client logic
Add complete update mechanism for client-side updates:
- backend/update.py: Core update logic (check, apply, rollback, status/logs)
- backend/app.py: REST API endpoints (GET /update/status, POST /update/check, POST /update/apply, POST /update/rollback, GET /update/logs)
- backend/models.py: Pydantic models for update API responses
- backend/settings.py: Update config (status/log file paths)
- scripts/rollback_client.sh: Rollback script for failed updates
- scripts/update_client.sh: Enhanced update client script
- CLAUDE.md: Documentation for future Claude Code instances

Complete US_000026-028 and TASK_000027-029:
- US_000026: Client pulls updates from remote service
- US_000027: Client verifies and applies updates atomically
- US_000028: Client reports update status to backend

All endpoints require authentication. Updates run asynchronously.
Documentation updated per SOP (CHANGELOG, PROJECT_STATUS, stories/tasks).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-30 11:18:34 +01:00

7.3 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

Safe Kiddo Daemon (SKD) is a FastAPI-based service for managing local user accounts on kids' laptops. It provides account locking/unlocking with countdown notifications, optional sound alerts, and shutdown capabilities. The service exposes a REST API with bearer token auth (PAM or OIDC) and serves a minimal web UI.

Commands

Development

# Run the service manually (uses .venv, binds to 0.0.0.0:80)
./scripts/run.sh

# Install service and dependencies
sudo make install

# Service management
sudo make up      # Start service
sudo make down    # Stop service
sudo make update  # Pull latest from git, reinstall deps, restart

# Generate/set API token
make token

# Health check (requires token)
make healthcheck

Installation & Deployment

# Full install (creates service user, venv, systemd unit, PAM config)
sudo ./scripts/install.sh

# Deploy to remote host (requires deploy_hosts.yml)
./scripts/deploy.sh <host-name>

# Manual update on target
ssh user@target 'cd /opt/sk && ./scripts/update.sh'

Testing

# Python syntax validation
python -m py_compile backend/*.py

# Run specific tests (no formal test runner yet; tests/ is empty)
# Use curl for API testing:
token=$(curl -s -X POST -H "Content-Type: application/json" \
  -d '{"username":"root","password":"..."}' \
  http://localhost/login | jq -r .token)
curl -H "Authorization: Bearer $token" http://localhost/users

Architecture

Core Structure

  • backend/: FastAPI application
    • app.py: Main FastAPI app with route handlers
    • actions.py: User management actions (lock/unlock, notifications, shutdown logic)
    • auth.py: PAM authentication, JWT tokens, user/group authorization checks
    • oidc.py: OIDC client (dynamic discovery, token exchange, claims validation)
    • update.py: Update client logic (check/status/logs, triggers async update/rollback scripts)
    • settings.py: Environment-based configuration (Settings class, singleton via lru_cache)
    • models.py: Pydantic models for API requests/responses
    • templates/: Jinja2 templates for web UI
  • scripts/: Deployment and lifecycle scripts
    • install.sh: System setup (user, venv, systemd, PAM config)
    • run.sh: Manual service start
    • update.sh: Local git pull and service restart
    • update_client.sh: Full update flow with backup/rollback
    • rollback_client.sh: Restore from backup if update fails
    • deploy.sh: SSH-based deployment to remote hosts
    • register_oidc_client.sh: OIDC dynamic client registration helper
  • sk.sh: Legacy bash script (CLI fallback for direct SSH use)
  • src/: Hexagonal architecture skeleton (core/ports/adapters/ui) - currently empty placeholders
  • docs/: Detailed specs for OIDC validation, update API, status/log formats
  • Makefile: Convenience targets for install, service control, updates, token management

Key Architectural Patterns

Dual Authentication: PAM-based local auth (root/sudo users) is always available; OIDC is optional if SKD_OIDC_ISSUER, SKD_OIDC_CLIENT_ID, and SKD_OIDC_CLIENT_SECRET are configured. Both modes issue JWT bearer tokens.

Settings Management: All config via environment variables (loaded from /etc/skd/env in production). settings.py provides a singleton Settings instance via get_settings() using lru_cache. FastAPI dependencies inject settings into route handlers.

Action Execution: actions.py wraps all privileged operations (usermod, pkill, shutdown) via _run() helper. Dry-run mode (SKD_DRY_RUN=true) logs commands without executing them.

Update Flow: update.py checks remote update service for new versions, writes status to JSON files, and triggers async scripts (update_client.sh, rollback_client.sh) that create backups, apply updates, and handle rollbacks on failure.

Authorization: auth.py checks both user allowlists (SKD_AUTH_ALLOWED_USERS) and group membership (SKD_AUTH_ALLOWED_GROUPS, defaults to sudo). UID 0 (root) always allowed for PAM. OIDC validates against preferred_username, email, or sub claims.

Manageable Users: Only system users with UID >= 1000, real shells (not nologin/false), and optional allowlist (SKD_ALLOWED_USERS) are exposed via API. Root accounts are never manageable.

Configuration

Deployment config lives in /etc/skd/env (see env.example in repo root):

  • SKD_AUTH_SECRET: HMAC secret for JWT signing (must be strong in production)
  • SKD_AUTH_ALLOWED_USERS: Comma-separated user allowlist (for login and OIDC claims)
  • SKD_AUTH_ALLOWED_GROUPS: Groups whose members may log in (PAM only, default sudo)
  • SKD_AUTH_PAM_SERVICE: PAM service name (Ubuntu/Debian use skd, others may use login)
  • SKD_OIDC_*: OIDC provider config (ISSUER, CLIENT_ID, CLIENT_SECRET, REDIRECT_URI, SCOPES)
  • SKD_ALLOWED_USERS: Comma-separated list of manageable system accounts (optional)
  • SKD_DRY_RUN: If true, logs all privileged commands without executing
  • SKD_UPDATE_*: Update service URL, token, interval, status/log file paths

Important Workflows

Disable User Flow

  1. API call to /users/{username}/disable with optional {countdown, sound, message}
  2. actions.disable_user() locks account via usermod -L
  3. If user logged in: sends desktop notifications, plays sound (if enabled), countdown loop with periodic reminders
  4. Terminates sessions via pkill -KILL -u
  5. Triggers shutdown now only if user was logged in

OIDC Login Flow

  1. User accesses /login/oidc/start → redirected to provider with state cookie
  2. Provider redirects to /login/oidc/callback with code + state
  3. Validates state, exchanges code for tokens, extracts username from claims
  4. Issues JWT session cookie if user in allowlist

Update Flow

  1. check_update() polls remote update service for latest manifest (version, artifact_url, sha256)
  2. start_update() writes "in_progress" status, launches update_client.sh in background
  3. Script creates backup, downloads artifact, verifies checksum, installs, restarts service
  4. On failure: rollback_client.sh restores from backup
  5. Status/logs written to JSON files at SKD_UPDATE_STATUS_FILE and SKD_UPDATE_LOG_FILE

Security Considerations

  • Service runs as root by default (required for PAM, usermod, pkill, shutdown). Limit exposure via firewall.
  • Set strong SKD_AUTH_SECRET and rotate by changing value + restarting service.
  • Restrict API/Web UI to LAN/VPN; consider mTLS or IP allowlisting.
  • skd user/group created by install script; consider sudoers rules to limit privileges to specific commands.
  • OIDC redirect URI must match exactly (no wildcards); re-register client if host/port changes.
  • Validate TLS certificates in production; self-signed certs require CA trust or fallback to PAM.

Notes

  • Legacy sk.sh remains for emergency CLI fallback; API is preferred for all operations.
  • src/ hexagonal architecture skeleton is currently unused; logic lives in backend/.
  • tests/ directory exists but is empty; use manual curl-based API testing.
  • Both German and English comments exist in code; favor English going forward.
  • Deployment via deploy.sh supports both YAML (deploy_hosts.yml) and JSON host configs.
  • PAM service file (/etc/pam.d/skd) created by scripts/install.sh on Ubuntu/Debian; other distros may need manual setup.