homelab infrastructure services

Example Development Workflows#

This guide demonstrates practical workflows for teams using tinsnip with cross-platform development environments.

Scenario Setup#

Hardware & Users#

  • u1: Developer on Windows
  • u2: Developer on macOS
  • s1: Ubuntu server (shared development environment)
  • DS412plus: Synology NAS for persistent storage

Service Architecture#

Production Services (E=0):
- p1-prod (10100): Core service
- p2-prod (10200): API service  
- p3-prod (10300): Database service

Test Services (E=1):
- t3-test (10310): Test instance of p3

Development Work:
- u1 creating d4 service (depends on p2-prod and t3-test)
- u2 modifying t3 service

Prerequisites#

On s1 (Ubuntu Server)#

# Install tinsnip
curl -fsSL "https://tangled.sh/dynamicalsystem.com/tinsnip/raw/main/install.sh?$(date +%s)" | bash
cd ~/.local/opt/dynamicalsystem.tinsnip

# Create user accounts for developers
sudo useradd -m -s /bin/bash -G sudo,docker u1
sudo useradd -m -s /bin/bash -G sudo,docker u2
sudo passwd u1
sudo passwd u2

# Fix npm permissions for each user
for user in u1 u2; do
    sudo -u $user -i bash << 'EOF'
    mkdir ~/.npm-global
    npm config set prefix '~/.npm-global'
    echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.bashrc
EOF
done

On Developer Machines#

Windows (u1) - PowerShell:

# Install VSCode
winget install Microsoft.VisualStudioCode

# Install Remote-SSH extension
code --install-extension ms-vscode-remote.remote-ssh

# Configure SSH
New-Item -Path ~/.ssh/config -ItemType File -Force
Add-Content ~/.ssh/config @"
Host s1-dev
    HostName s1.example.com
    User u1
    LocalForward 10420 localhost:10420
    LocalForward 10421 localhost:10421
    LocalForward 10200 localhost:10200
    LocalForward 10350 localhost:10350
"@

macOS (u2) - Terminal:

# Install VSCode
brew install --cask visual-studio-code

# Install Remote-SSH extension
code --install-extension ms-vscode-remote.remote-ssh

# Configure SSH
cat >> ~/.ssh/config << EOF
Host s1-dev
    HostName s1.example.com
    User u2
    LocalForward 10330 localhost:10330
    LocalForward 10331 localhost:10331
    ControlMaster auto
    ControlPath ~/.ssh/cm-%r@%h:%p
EOF

Workflow 1: u1 Creates New Service (d4)#

Step 1: Create Service Infrastructure#

# SSH to s1
ssh s1-dev

# Create d4 development environment
cd ~/.local/opt/dynamicalsystem.tinsnip
tin machine create d4 dev DS412plus

# Output:
# Service user: d4-dev
# UID: 10420
# Ports: 10420-10429
# NFS mount: /mnt/d4-dev

Step 2: Set Up Service Code#

# As u1 on s1
cd /mnt/d4-dev/service

# Create service structure
cat > docker-compose.yml << 'EOF'
version: '3.8'

services:
  d4:
    build: .
    container_name: d4-dev
    ports:
      - "10420:8000"
      - "10421:8001"  # Admin UI
    environment:
      - TIN_SERVICE_NAME=d4
      - TIN_SERVICE_ENV=dev
      - TIN_SERVICE_UID=10420
      - P2_API_URL=http://p2-prod:10200
      - T3_DB_URL=postgres://t3-test:10310/d4_dev
    volumes:
      - ./src:/app/src:delegated  # Hot reload
      - /mnt/d4-dev/data:/data
      - /mnt/d4-dev/config:/config
    networks:
      - tinsnip-network

networks:
  tinsnip-network:
    external: true
EOF

# Create Dockerfile
cat > Dockerfile << 'EOF'
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
CMD ["npm", "run", "dev"]
EOF

Step 3: VSCode Development#

On Windows (u1):

# Open VSCode with Remote-SSH
code --folder-uri vscode-remote://ssh-remote+s1-dev/mnt/d4-dev/service

# This opens VSCode connected to s1
# All file edits happen directly on s1
# Terminal runs on s1

Step 4: Claude Code Integration#

In VSCode integrated terminal (running on s1):

# Start Claude Code in the service directory
cd /mnt/d4-dev/service
claude code

# Ask Claude Code to help with the service
# "Create an Express API that connects to p2 service and t3 database"
# "Add error handling for the external service calls"
# "Generate TypeScript interfaces for the API responses"

Step 5: Test Service#

# In VSCode terminal (on s1)
docker compose up -d
docker compose logs -f

# In Windows browser (port forwarded via SSH)
# http://localhost:10420 - Main service
# http://localhost:10421 - Admin UI

Workflow 2: u2 Modifies Existing Service (t3)#

Step 1: Create Staging Environment#

# SSH to s1
ssh s1-dev

# Create t3 staging environment for modifications
cd ~/.local/opt/dynamicalsystem.tinsnip
tin machine create t3 staging DS412plus

# Output:
# Service user: t3-staging
# UID: 10330
# Ports: 10330-10339

Step 2: Copy Current t3 Code#

# Clone t3 from test to staging
sudo cp -r /mnt/t3-test/service/* /mnt/t3-staging/service/
sudo chown -R t3-staging:t3-staging /mnt/t3-staging/service

Step 3: VSCode Development#

On macOS (u2):

# Open VSCode with Remote-SSH
code --folder-uri vscode-remote://ssh-remote+s1-dev/mnt/t3-staging/service

Step 4: Make Modifications with Claude Code#

In VSCode integrated terminal:

cd /mnt/t3-staging/service
claude code

# Work with Claude Code
# "Add caching layer to the database queries"
# "Implement connection pooling"
# "Add metrics endpoint for Prometheus"

Step 5: Test Changes#

# Build and run
docker compose build --no-cache
docker compose up -d

# Run tests
docker compose exec t3 npm test

# Check metrics endpoint
curl http://localhost:10332/metrics

Workflow 3: Integration Testing#

Create Integration Environment#

# On s1, create shared integration environment
tin machine create integration demo DS412plus
# UID: 10440, Ports: 10440-10449

Deploy Both Services#

# Deploy u2's t3 changes
cd /mnt/integration-demo/service
cp -r /mnt/t3-staging/service/t3 ./
cd t3
docker compose up -d

# Deploy u1's d4 service pointing to new t3
cd /mnt/integration-demo/service
cp -r /mnt/d4-dev/service/d4 ./
cd d4

# Update d4 to use staging t3
sed -i 's/t3-test:10310/t3-staging:10330/g' docker-compose.yml
docker compose up -d

Run Integration Tests#

# Create test script
cat > /mnt/integration-demo/test.sh << 'EOF'
#!/bin/bash
echo "Testing d4 -> t3 integration..."

# Test d4 API
curl -f http://localhost:10440/health || exit 1

# Test t3 connection
curl -f http://localhost:10440/api/database/status || exit 1

echo "Integration tests passed!"
EOF

chmod +x /mnt/integration-demo/test.sh
./test.sh

Workflow 4: Collaborative Debugging#

Using tmux for Shared Sessions#

# u1 starts tmux session on s1
ssh s1-dev
tmux new -s debug-d4

# Split panes
# Ctrl-b % (vertical split)
# Ctrl-b " (horizontal split)

# Pane 1: Docker logs
docker compose logs -f d4

# Pane 2: Claude Code
claude code

# Pane 3: Testing
watch curl http://localhost:10420/health
# u2 joins the session
ssh s1-dev
tmux attach -t debug-d4

# Both developers see the same screen
# Can take turns typing

Shared Claude Code Context#

# Create shared context file
cat > /mnt/integration-demo/CLAUDE.md << 'EOF'
# Integration Context

## Services
- d4: New service by u1, ports 10420-10429
- t3: Modified by u2, ports 10330-10339

## Dependencies
- d4 -> p2 (10200) for API
- d4 -> t3 (10330) for database

## Current Issues
- Connection timeout between d4 and t3
- Need to add retry logic

## Test Command
curl http://localhost:10440/api/test
EOF

# Both developers can reference
cd /mnt/integration-demo
claude code
# "Read CLAUDE.md and help debug the connection timeout"

Workflow 5: Service Promotion#

Test to Production Pipeline#

# 1. Freeze staging changes
cd /mnt/t3-staging/service
git add .
git commit -m "Add caching and connection pooling"
git push origin t3-staging

# 2. Deploy to test environment
tin machine create t3 test DS412plus
cd /mnt/t3-test/service
git pull origin t3-staging
docker compose up -d

# 3. Run test suite
docker compose exec t3 npm run test:integration

# 4. Promote to production (if tests pass)
cd /mnt/t3-prod/service
git pull origin t3-staging
docker compose up -d --scale t3=2  # Rolling update

Tips and Tricks#

1. Port Forwarding Alias#

Windows PowerShell:

function Connect-Dev {
    ssh -L 10420:localhost:10420 `
        -L 10421:localhost:10421 `
        -L 10200:localhost:10200 `
        -L 10350:localhost:10350 `
        u1@s1.example.com
}

macOS/Linux Bash:

alias s1dev='ssh -L 10330:localhost:10330 -L 10331:localhost:10331 s1-dev'

2. Quick Service Status#

# Create status script
cat > ~/status.sh << 'EOF'
#!/bin/bash
echo "=== Service Status ==="
for service in d4-dev t3-staging integration-demo; do
    if [[ -d /mnt/$service/service ]]; then
        echo -n "$service: "
        cd /mnt/$service/service
        docker compose ps --format "table {{.State}}" | tail -n1
    fi
done
EOF
chmod +x ~/status.sh

3. Claude Code Templates#

# Create templates for common tasks
mkdir -p ~/.claude-templates

cat > ~/.claude-templates/debug-docker.md << 'EOF'
Analyze these Docker logs and identify the issue:

[PASTE LOGS]

Service configuration:
```yaml
[PASTE docker-compose.yml]

EOF

Use template#

cat ~/.claude-templates/debug-docker.md | claude code


### 4. VSCode Workspace Settings

Create `.vscode/settings.json` in each service:
```json
{
  "terminal.integrated.cwd": "/mnt/d4-dev/service",
  "files.watcherExclude": {
    "**/node_modules/**": true,
    "**/data/**": true
  },
  "docker.host": "unix:///var/run/docker.sock",
  "remote.autoForwardPorts": true,
  "remote.autoForwardPortsSource": "process"
}

Troubleshooting#

Permission Denied on Docker#

# Add user to docker group
sudo usermod -aG docker u1
newgrp docker

NFS Mount Issues#

# Check NFS exports on NAS
ssh admin@DS412plus
showmount -e localhost

# Test mount manually
sudo mount -t nfs DS412plus:/volume1/topsheet/gazette/test /tmp/test

Port Already in Use#

# Find process using port
sudo lsof -i :10420
sudo netstat -tulpn | grep 10420

# Kill process if needed
sudo kill $(sudo lsof -t -i:10420)

Claude Code Not Found#

If claude code command is not available:

  1. Ensure you're using the desktop app or have API access configured
  2. Alternative: Use Claude.ai web interface with copy/paste
  3. Or use API integration scripts as shown in the examples

Summary#

This workflow enables:

  • Cross-platform development (Windows/macOS developers on Linux server)
  • Service isolation using tinsnip's SMEP convention
  • Fast iteration with VSCode Remote-SSH and Docker
  • AI assistance with Claude Code integration
  • Predictable networking with SMEP port allocation
  • Persistent storage via NFS mounts

The key is using s1 as a shared development server with isolated environments per developer and service, while maintaining local IDE experience through VSCode Remote-SSH.