Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
569e341
Add AWS Bedrock support, SSL certificates, SSH agent, and internal DNS
kisnandor2 Dec 8, 2025
86a8f3c
Update README with new config options
kisnandor2 Dec 8, 2025
ce2c294
Add SSL certificate import for Java keystore in containers
kisnandor2 Dec 11, 2025
1a5ff37
Add Android SDK mounting support for containers
kisnandor2 Dec 12, 2025
48cfe43
Update README with SSL certificates and Android SDK config
kisnandor2 Dec 12, 2025
0d1f9a1
Add daemon auto-start, notifications fix, and config options
kisnandor2 Dec 12, 2025
239324c
Make AWS firewall rules conditional on Bedrock/AWS config
kisnandor2 Jan 12, 2026
fdf3b3a
Merge remote-tracking branch 'origin/main'
kisnandor2 Jan 13, 2026
aa61c09
Add batch command, shell completion, and waiting status indicator
kisnandor2 Jan 15, 2026
d686097
Add SSH known_hosts mounting to avoid host key prompts
kisnandor2 Jan 15, 2026
8acf2d0
Add TUI improvements: CREATED column, parallel loading, conditional AUTH
kisnandor2 Jan 16, 2026
a09cdec
Add GitHub Enterprise support for maestro auth
kisnandor2 Jan 16, 2026
5796a93
Improve batch task analysis to respect logical groupings
kisnandor2 Jan 19, 2026
4ffff41
Add --extra-command flag to batch mode for follow-up instructions
kisnandor2 Jan 19, 2026
fdd9fba
Fix OpenJDK download for ARM64 architecture
kisnandor2 Jan 19, 2026
380833d
Merge branch 'main' into feat/batch-and-some-fixes
kisnandor2 Jan 19, 2026
2bde582
Add copy progress display and optimize firewall/sync performance
kisnandor2 Jan 20, 2026
e7382a6
Remove useless 'Waiting...' phase from batch progress display
kisnandor2 Jan 20, 2026
a1a54bc
Add .maestroignore support for project-level copy exclusions
kisnandor2 Jan 20, 2026
f54799c
Merge pull request #1 from kisnandor2/feat/batch-and-some-fixes
kisnandor2 Jan 20, 2026
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
56 changes: 47 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
# Copyright 2025 Christopher O'Connell
# All rights reserved

.PHONY: build install docker signing-image clean test help release release-snapshot license-check
.PHONY: build install install-completion docker signing-image clean test help release release-snapshot license-check

# Default target
help:
@echo "MCL Build Targets:"
@echo " make build - Build the maestro binary"
@echo " make docker - Build the Docker image locally"
@echo " make signing-image - Build the code signing Docker image"
@echo " make install - Install maestro to /usr/local/bin (requires sudo)"
@echo " make test - Run tests"
@echo " make clean - Remove built binaries"
@echo " make all - Build everything (binary + docker)"
@echo " make license-check - Check/add Apache 2.0 headers to source files"
@echo " make build - Build the maestro binary"
@echo " make docker - Build the Docker image locally"
@echo " make signing-image - Build the code signing Docker image"
@echo " make install - Install maestro to /usr/local/bin (requires sudo)"
@echo " make install-completion - Install shell completion for current shell"
@echo " make test - Run tests"
@echo " make clean - Remove built binaries"
@echo " make all - Build everything (binary + docker)"
@echo " make license-check - Check/add Apache 2.0 headers to source files"
@echo ""
@echo "Release Targets:"
@echo " make release-preflight - Check release prerequisites"
Expand Down Expand Up @@ -55,6 +56,43 @@ signing-image:
# Install to system PATH (run 'make build' first, then 'sudo make install')
install:
cp bin/maestro /usr/local/bin/
@echo ""
@echo "Run 'make install-completion' to enable shell autocompletion"

# Install shell completion for current shell
install-completion:
@if [ ! -f bin/maestro ]; then \
echo "Error: bin/maestro not found. Run 'make build' first."; \
exit 1; \
fi
@SHELL_NAME=$$(basename "$$SHELL"); \
case "$$SHELL_NAME" in \
bash) \
echo "Installing bash completion..."; \
mkdir -p ~/.local/share/bash-completion/completions; \
bin/maestro completion bash > ~/.local/share/bash-completion/completions/maestro; \
echo "Installed to ~/.local/share/bash-completion/completions/maestro"; \
echo "Run 'source ~/.local/share/bash-completion/completions/maestro' or restart your shell"; \
;; \
zsh) \
echo "Installing zsh completion..."; \
mkdir -p ~/.zsh/completions; \
bin/maestro completion zsh > ~/.zsh/completions/_maestro; \
echo "Installed to ~/.zsh/completions/_maestro"; \
echo "Add 'fpath=(~/.zsh/completions \$$fpath)' to ~/.zshrc if not already present"; \
echo "Then run 'autoload -U compinit && compinit' or restart your shell"; \
;; \
fish) \
echo "Installing fish completion..."; \
mkdir -p ~/.config/fish/completions; \
bin/maestro completion fish > ~/.config/fish/completions/maestro.fish; \
echo "Installed to ~/.config/fish/completions/maestro.fish"; \
;; \
*) \
echo "Unknown shell: $$SHELL_NAME"; \
echo "Run 'maestro completion --help' for manual installation instructions"; \
;; \
esac

# Run tests
test:
Expand Down
144 changes: 132 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,23 +58,138 @@ This stores credentials in `~/.maestro/` and shares them (read-only) with all co

### 2. Configure (Optional)

Edit `~/.maestro/config.yml` to add additional folders and network domains:
Edit `~/.maestro/config.yml` to customize your setup:

```yaml
firewall:
allowed_domains:
- github.com
- api.anthropic.com
# Add your domains here
# For corporate networks with internal DNS (Zscaler, VPN, etc.)
internal_dns: "10.0.0.1"
internal_domains:
- "internal.company.com"

sync:
additional_folders:
- ~/Documents/Code/mcp-servers
- ~/Documents/Code/helpers
# Compression: gzip the tar stream when copying files to containers
# - true (default): smaller transfer, good for remote Docker or slow I/O
# - false: faster for large local projects (8GB+), skips compression overhead
compress: false

# Git user for commits inside containers
git:
user_name: "Your Name"
user_email: "you@example.com"

# SSH agent forwarding for git authentication (keys stay on host)
ssh:
enabled: true
known_hosts_path: "~/.ssh/known_hosts" # mount host's known_hosts to avoid prompts

# GitHub CLI integration (for PRs, issues, etc.)
github:
enabled: true
hostname: "github.mycompany.com" # For GitHub Enterprise (omit for github.com)

# AWS Bedrock support (alternative to Anthropic API)
aws:
enabled: true
profile: "your-aws-profile"
region: "us-east-1"

bedrock:
enabled: true
model: "anthropic.claude-sonnet-4-20250514-v1:0"

# SSL certificates for corporate HTTPS inspection
ssl:
certificates_path: "~/.maestro/certificates"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess they're just public certificates. I wonder if there is any risk here that I haven't thought of?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is mostly due to the fact that these need to be part of the java cacerts (it's mainly for Java/Android builds).

We could simplify by importing from the already-mounted /etc/ssl/certs instead of requiring a separate ~/.maestro/certificates directory.

The current approach is:

  1. Conservative - avoid touching Java's default cacerts unnecessarily
  2. Avoiding duplicates - Java ships with ~100+ common CAs already
  3. Startup speed - importing hundreds of certs takes time

But for corporate proxies (Zscaler, etc.), their CA cert IS typically in /etc/ssl/certs on the host. So we could just import from there.

Simpler approach:

  # In container startup, import any certs from /etc/ssl/certs 
  # that aren't already in Java's keystore
  for cert in /etc/ssl/certs/*.crt /etc/ssl/certs/*.pem; do
      keytool -importcert -noprompt -trustcacerts \
          -alias "$(basename $cert)" \
          -file "$cert" \
          -keystore $JAVA_HOME/lib/security/cacerts \
          -storepass changeit 2>/dev/null || true  # ignore duplicates
  done

This would:

  • Eliminate the need for ssl.certificates_path config
  • Automatically work with any corporate CA on the host
  • Be idempotent (duplicates just fail silently)
  • Add ~5-10 seconds to container startup to process all certs.


# Android SDK for mobile development
android:
sdk_path: "~/Android/Sdk"

# Container defaults
containers:
default_return_to_tui: true # Auto-check "Return to TUI" when creating containers

# Daemon and notification settings
daemon:
check_interval: "10s" # How often to check containers (default: 30m)
notifications:
enabled: true
attention_threshold: "5s" # Notify after this duration of waiting
notify_on:
- attention_needed # When Claude waits for input
- token_expiring # When auth token is expiring
```

You can also set firewall rules from the text UI using the `f` shortcut.

#### AWS Bedrock Setup

To use Claude via AWS Bedrock instead of the Anthropic API:

1. Configure your AWS profile with Bedrock access
2. Enable bedrock in config (see above)
3. Run `maestro auth` to set up AWS SSO login
4. Containers will automatically use Bedrock for Claude

#### Corporate Network / VPN Setup

If you're behind a corporate proxy (Zscaler, etc.) or need to access internal resources:

1. Set `firewall.internal_dns` to your internal DNS server
2. Add internal domains to `firewall.internal_domains`
3. Host SSL certificates are automatically mounted for HTTPS inspection

#### SSL Certificates

For corporate environments with HTTPS inspection (Zscaler, etc.), place your CA certificates in the configured path:

1. Create the certificates directory: `mkdir -p ~/.maestro/certificates`
2. Copy your corporate CA certificates (`.crt`, `.pem` files) to this directory
3. Certificates are automatically imported into both the system trust store and Java keystore inside containers

#### Android SDK

For Android/mobile development, mount your host Android SDK into containers:

1. Set `android.sdk_path` to your SDK location (e.g., `~/Android/Sdk`)
2. The SDK will be mounted read-only at `/opt/android-sdk` inside containers
3. Environment variables (`ANDROID_HOME`, `ANDROID_SDK_ROOT`) are automatically configured

#### Project-Level Exclusions (.maestroignore)

Create a `.maestroignore` file in your project root to exclude files/directories when copying to containers. This is useful for large projects with build artifacts that shouldn't be transferred.

```bash
# .maestroignore - exclude patterns (like .gitignore)
# Comments start with #

# Android/Gradle build artifacts
build
.gradle
.idea
.cxx
.kotlin

# Other common exclusions
dist
target
__pycache__
*.log
```

**Notes:**
- `node_modules` and `.git` are always excluded by default
- Each line is passed to `tar --exclude=`
- Empty lines and lines starting with `#` are ignored

### 3. Create Your First Container

```bash
Expand Down Expand Up @@ -123,22 +238,27 @@ When connected via `maestro connect`:

_Note: Not tested on Windows._

Start the daemon to monitor containers and get desktop notifications:
The daemon monitors containers and sends desktop notifications when Claude needs your attention. It **auto-starts** when you launch the TUI (`maestro`), but you can also manage it manually:

```bash
maestro daemon start

# Check status
maestro daemon status

# View logs
maestro daemon logs
maestro daemon start # Start manually
maestro daemon stop # Stop the daemon
maestro daemon status # Check status
maestro daemon logs # View logs
```

The daemon monitors:
- Token expiration (warns when < 1 hour remaining)
- Container attention needs (bell indicators)
- Automatic health checks every 30 minutes
- **Attention needs** - Notifies when Claude is waiting for input (configurable delay)
- **Token expiration** - Warns when auth token is expiring soon
- **Container health** - Periodic checks based on `check_interval`

Configure notification speed in `~/.maestro/config.yml`:
```yaml
daemon:
check_interval: "10s" # Check every 10 seconds (default: 30m)
notifications:
attention_threshold: "5s" # Notify after 5s of waiting
```

## Container Status

Expand Down
60 changes: 53 additions & 7 deletions assets/init-firewall.sh
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,40 @@ echo "server=/.githubusercontent.com/8.8.8.8" >> "$DNSMASQ_CONF"
echo "ipset=/.anthropic.com/allowed-domains" >> "$DNSMASQ_CONF"
echo "server=/.anthropic.com/8.8.8.8" >> "$DNSMASQ_CONF"

# Add wildcard entries for AWS (only if AWS/Bedrock is enabled)
# This is controlled by /etc/aws-enabled.txt which is written by maestro when aws.enabled or bedrock.enabled is true
AWS_ENABLED_FILE="/etc/aws-enabled.txt"
if [ -f "$AWS_ENABLED_FILE" ]; then
echo "AWS/Bedrock enabled - adding AWS domain rules"
echo "ipset=/.amazonaws.com/allowed-domains" >> "$DNSMASQ_CONF"
echo "server=/.amazonaws.com/8.8.8.8" >> "$DNSMASQ_CONF"
echo "ipset=/.awsapps.com/allowed-domains" >> "$DNSMASQ_CONF"
echo "server=/.awsapps.com/8.8.8.8" >> "$DNSMASQ_CONF"
else
echo "AWS/Bedrock not enabled - skipping AWS domain rules"
fi

# Configure internal DNS for corporate networks (Zscaler, VPN, etc.)
INTERNAL_DNS_FILE="/etc/internal-dns.txt"
INTERNAL_DOMAINS_FILE="/etc/internal-domains.txt"
if [ -f "$INTERNAL_DNS_FILE" ] && [ -f "$INTERNAL_DOMAINS_FILE" ]; then
INTERNAL_DNS=$(cat "$INTERNAL_DNS_FILE")
if [ -n "$INTERNAL_DNS" ]; then
echo "Configuring internal DNS server: $INTERNAL_DNS"
while read -r domain; do
[ -z "$domain" ] && continue
echo " Routing $domain via internal DNS"
echo "ipset=/$domain/allowed-domains" >> "$DNSMASQ_CONF"
echo "server=/$domain/$INTERNAL_DNS" >> "$DNSMASQ_CONF"
# Also add wildcard for subdomains
echo "ipset=/.$domain/allowed-domains" >> "$DNSMASQ_CONF"
echo "server=/.$domain/$INTERNAL_DNS" >> "$DNSMASQ_CONF"
done < "$INTERNAL_DOMAINS_FILE"
fi
elif [ -f "$INTERNAL_DNS_FILE" ]; then
echo "Warning: Internal DNS configured but no internal domains specified"
fi

# Start dnsmasq
echo "Starting dnsmasq..."
dnsmasq --conf-file="$DNSMASQ_CONF"
Expand All @@ -125,7 +159,7 @@ echo "nameserver 127.0.0.1" | tee /etc/resolv.conf > /dev/null
# Process GitHub API ranges and add them directly to ipset
# We do this because GitHub has many IPs and we want to ensure we catch them all
echo "Fetching GitHub IP ranges..."
gh_ranges=$(curl -s https://api.github.com/meta)
gh_ranges=$(curl -s --connect-timeout 5 --max-time 10 https://api.github.com/meta)
if [ -z "$gh_ranges" ]; then
echo "WARNING: Failed to fetch GitHub IP ranges - GitHub access may be limited"
else
Expand Down Expand Up @@ -193,24 +227,36 @@ iptables -A OUTPUT -j REJECT --reject-with icmp-admin-prohibited

echo "Firewall configuration complete"

# Verify firewall rules
# Verify firewall rules (run all checks in parallel)
echo "Verifying firewall rules..."
if curl --connect-timeout 5 https://example.com >/dev/null 2>&1; then

# Create temp files for results
VERIFY_DIR=$(mktemp -d)
trap "rm -rf $VERIFY_DIR" EXIT

# Run all verification tests in parallel
(curl --connect-timeout 3 --max-time 5 https://example.com >/dev/null 2>&1 && echo "fail" || echo "pass") > "$VERIFY_DIR/block" &
(curl --connect-timeout 3 --max-time 5 https://api.github.com/zen >/dev/null 2>&1 && echo "pass" || echo "fail") > "$VERIFY_DIR/github" &
(curl --connect-timeout 3 --max-time 5 https://api.anthropic.com >/dev/null 2>&1 && echo "pass" || echo "fail") > "$VERIFY_DIR/anthropic" &

# Wait for all background jobs
wait

# Check results
if [ "$(cat "$VERIFY_DIR/block")" = "fail" ]; then
echo "ERROR: Firewall verification failed - was able to reach https://example.com"
exit 1
else
echo "✓ Firewall blocking works - unable to reach https://example.com"
fi

# Verify allowed access
echo "Testing DNS resolution and access to whitelisted domains..."
if ! curl --connect-timeout 10 https://api.github.com/zen >/dev/null 2>&1; then
if [ "$(cat "$VERIFY_DIR/github")" = "fail" ]; then
echo "WARNING: Unable to reach https://api.github.com - GitHub access may be limited"
else
echo "✓ GitHub API access works"
fi

if ! curl --connect-timeout 10 https://api.anthropic.com >/dev/null 2>&1; then
if [ "$(cat "$VERIFY_DIR/anthropic")" = "fail" ]; then
echo "WARNING: Unable to reach https://api.anthropic.com - Anthropic access may be limited"
else
echo "✓ Anthropic API access works"
Expand Down
Loading