This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
SingBox for Magisk is a Magisk/KernelSU/APatch module providing transparent proxy functionality for Android devices using sing-box. The module supports three network modes (tproxy, redirect, tun) and manages iptables rules, policy routing, and process lifecycle for the sing-box proxy.
This is a Magisk module with the following architecture:
- Root-level scripts: Module lifecycle hooks (
customize.sh,service.sh,action.sh,build.sh) singbox/directory: Deployed to/data/adb/singboxon device, containing:bin/: sing-box and jq binariesscripts/: Core service management scripts (modular shell architecture)config.json: User's sing-box configurationsettings.ini: Module settings (network_mode, ipv6, ap_list)include.list/exclude.list: Application filtering (whitelist/blacklist)logs/: Runtime logs with automatic rotationui/: Web UI assets for sing-box dashboard
The codebase follows a modular shell script architecture with clear separation of concerns:
Core Dependencies (source order matters):
constants.sh # All constants and paths (must load first)
↓
utils.sh # Utility functions (depends on constants)
↓
config.sh # Configuration loading (depends on constants + utils)
↓
service.sh # Main service orchestration
iptables.sh # Network rules management
Key Scripts:
-
constants.sh: Centralized constants definition- Paths, ports, timeouts, network constants
- Intranet IPv4/IPv6 ranges
- Never modify these directly; they define the entire system's contract
-
utils.sh: Reusable utility functions- Logging with color-coded levels (info/warn/error/debug)
- Process management (check_process_running, safe_kill, force_kill)
- Log rotation (automatic when files exceed LOG_MAX_SIZE)
- File operations, validation helpers
-
config.sh: Configuration loading and validation- Loads
config.jsonusing jq to extract ports/inbound config - Loads
settings.inifor user preferences - Validates configuration and provides friendly error messages
- Builds intranet address lists including FakeIP ranges
- Loads
-
service.sh: Main service orchestration- Commands: start, stop, restart, force-stop, status, health
- Startup sequence: cleanup → validate config → start process → setup iptables → setup IPv6
- Health checks: process status, config integrity, log errors, network connectivity
-
iptables.sh: Network rules management- Implements three modes: redirect (TCP only), tproxy (TCP+UDP), tun (virtual interface)
- Creates custom chains: BOX_EXTERNAL, BOX_LOCAL, BOX_IP_V4, BOX_IP_V6
- Handles application filtering (include/exclude lists via UIDs)
- Policy routing setup for tproxy mode (fwmark-based routing)
- IPv6 support with parallel rule structures
-
diagnose.sh: System diagnostics (not shown but referenced) -
rmlimit.sh: Removes vendor network restrictions (called during cleanup)
The module has strict configuration validation with user-friendly error messages:
network_modeinsettings.inimust match inbound type inconfig.json- If mismatch detected, shows example configuration with detailed suggestions
- Validates ports, checks TUN device availability, falls back gracefully (TUN → tproxy)
- Config errors include context and actionable fixes, not just "failed"
Process Management:
- TUN mode runs with full root privileges (needs
CAP_NET_ADMINcapability) - Other modes (tproxy/redirect) use
busybox setuidgid root:net_adminfor security - Wait loops with
MAX_RETRIESandRETRY_INTERVALfor process startup verification - Graceful shutdown (SIGTERM) with fallback to force kill (SIGKILL) after timeout
- Always cleanup iptables rules before start to ensure clean state
iptables Architecture:
- Custom chains avoid polluting system chains
- Order matters: check established connections first (optimization)
- Bypass intranet traffic early to avoid routing loops
- Application filtering by UID (Android's package → UID mapping)
- Cleanup must remove chain references from main chains before deleting chains
Logging System:
- Dual output: stdout (colorized for TTY) + log files
- Automatic log rotation at 10MB with 3 backups
- Updates
module.propdescription in real-time for Magisk Manager visibility - Log levels: info (green), warn (yellow), error (red), debug (cyan)
IPv6 Handling:
- Completely separate rule set (ip6tables) paralleling IPv4
- Policy routing with IPv6 fwmark and route table 2024
- Can be disabled via
ipv6="false"insettings.ini - Adds unreachable rule when IPv6 disabled to prevent leaks
Application Filtering Priority:
exclude.list>include.list(exclude takes precedence)- Also reads config.json's
exclude_package[]/include_package[]arrays - Merged and deduplicated package lists
- Uses
pm list packages -Uto resolve package names → UIDs
Network Mode Implementation:
-
redirect mode: Uses
REDIRECTtarget in nat table (TCP only, UDP direct)- Redirects TCP to
redir_portfrom config.json - Simpler, better compatibility, but UDP not proxied
- Redirects TCP to
-
tproxy mode: Uses
TPROXYtarget in mangle table (TCP + UDP)- Requires policy routing setup (fwmark 16777216/16777216, table 2024)
- Marks packets → routing table → local delivery to tproxy_port
- Best performance and functionality (recommended)
-
tun mode: Uses virtual network interface
- Least iptables rules (just FORWARD chain accepts)
- sing-box handles routing via TUN interface
- Checks TUN device availability (
/dev/net/tun) - Sets TUN device permissions to 0666 for accessibility
- Runs sing-box with full root privileges (no setuidgid)
- Attempts SELinux context adjustment if chcon available
- Auto-fallback to tproxy if TUN not available
# Build the Magisk/KernelSU/APatch module ZIP
./build.shThis creates box_for_magisk-vX.X.X.zip by:
- Reading version from
module.prop - Zipping all files except
.git/,.github/,docs/,build.sh,README.md,LICENSE
Service Management:
# Start service with full logging
/data/adb/singbox/scripts/service.sh start
# Check status (shows uptime, memory, recent logs)
/data/adb/singbox/scripts/service.sh status
# Health check (comprehensive diagnostics)
/data/adb/singbox/scripts/service.sh health
# Graceful stop
/data/adb/singbox/scripts/service.sh stop
# Force stop (SIGKILL)
/data/adb/singbox/scripts/service.sh force-stop
# Restart (stop + reload config + start)
/data/adb/singbox/scripts/service.sh restartConfiguration Validation:
# Validate sing-box config without starting
/data/adb/singbox/bin/sing-box check -D /data/adb/singbox/ -C /data/adb/singboxManual iptables Management:
# Apply tproxy rules manually
/data/adb/singbox/scripts/iptables.sh tproxy
# Apply redirect rules
/data/adb/singbox/scripts/iptables.sh redirect
# Apply tun rules
/data/adb/singbox/scripts/iptables.sh tun
# Clear all rules
/data/adb/singbox/scripts/iptables.sh clearLog Inspection:
# Real-time service log
tail -f /data/adb/singbox/logs/box.log
# Real-time script execution log
tail -f /data/adb/singbox/logs/run.log
# Check for errors
grep ERROR /data/adb/singbox/logs/box.log | tail -20Debugging iptables:
# View mangle table rules (tproxy mode)
iptables -t mangle -L -n -v
# View nat table rules (redirect mode)
iptables -t nat -L -n -v
# Check policy routing
ip rule list
ip route show table 2024
# IPv6 routing (if enabled)
ip -6 rule list
ip -6 route show table 2024Process Inspection:
# Find sing-box process
ps | grep sing-box
# Check process ownership
ls -l /proc/$(pidof sing-box)/
# Memory usage
cat /proc/$(pidof sing-box)/status | grep VmRSS- Always maintain source order:
constants.sh→utils.sh→config.shmust load in this order - Test all three network modes: changes to iptables.sh must work for redirect, tproxy, and tun
- Handle both IPv4 and IPv6: most networking code needs parallel IPv6 implementation
- Use existing utility functions: Don't reinvent logging, process checks, or file operations
- Validate early, fail gracefully: Check preconditions and provide actionable error messages
- Clean up resources: Always cleanup iptables rules and policy routing on stop/error
Adding a new configuration option:
- Add constant to
constants.shif it's system-wide - Add loading logic to
config.sh(either from settings.ini or config.json) - Add validation with user-friendly error messages
- Export the variable for other scripts to use
- Update
show_config_summary()to display the new option
Adding a new service command:
- Add case in
service.shmain() function - Create a dedicated function following the naming pattern
- Ensure proper logging at info/warn/error levels
- Update usage message in the
*)case
Modifying iptables rules:
- Test changes with
iptables.shdirectly first (not through service.sh) - Verify cleanup works correctly (rules fully removed)
- Check that rules don't cause routing loops
- Test with both include and exclude lists populated
- Verify IPv6 behavior when
ipv6="true"
- Use
set -ecarefully: Only in iptables.sh with proper trap handlers - Quote all variables: Use
"${variable}"not$variableto handle spaces - Check command existence: Use
command_existsbefore calling optional commands - Avoid bashisms: This is POSIX sh, not bash (use
[ ]not[[ ]], avoid arrays for most uses) - Use
busyboxcommands: Prefix withbusyboxwhen available for consistency - Always log operations: Use
log info/warn/errorliberally for debugging - Handle Android quirks: Android's shell environment is limited (no
pidof, limiteddf, etc.)
- iptables chain ordering: Wrong order breaks traffic or causes loops
- Policy routing setup: Incorrect fwmark or table number breaks tproxy mode
- Process startup wait logic: Too short = false negatives, too long = slow starts
- Configuration validation: Must catch mismatches between settings.ini and config.json
- IPv6 cleanup: Easy to forget IPv6 rules when cleaning up IPv4 rules
- Application UID resolution: Package manager queries are Android-version specific
module.prop: Module metadata (id, version, description) - update versionCode for each releasesingbox/settings.ini: User-editable settings (network_mode, ipv6, ap_list)singbox/config.json: User's sing-box configuration (not in repo, user provides)singbox/scripts/constants.sh: System constants (modify with extreme care)customize.sh: Magisk install hook (runs during module installation)service.sh: Magisk boot hook (runs at device boot after network ready)action.sh: Quick toggle script for manual service start/stop
Before committing changes:
- Test all three network modes (redirect, tproxy, tun)
- Test with IPv6 enabled and disabled
- Test with include.list and exclude.list populated
- Verify clean startup (no leftover rules from previous runs)
- Verify clean shutdown (all rules removed, routes cleaned)
- Check logs for errors or warnings
- Test configuration validation (intentionally break config.json)
- Test TUN fallback (make /dev/net/tun unavailable)
- Verify log rotation works (force log file > 10MB)
- Test health check command
- Forgetting to cleanup iptables before start: Always clear rules first to avoid duplicates
- Not testing IPv6 code paths: IPv6 logic often parallels IPv4 but has subtle differences
- Assuming busybox commands exist: Android environments vary; use
command_existschecks - Hardcoding paths: Always use constants from
constants.sh - Not handling config.json inbound mismatches: Users frequently set wrong network_mode
- Ignoring process startup timing: sing-box takes time to start; wait and verify
- Breaking module.prop updates: The
sedcommand for description must handle special chars - Not preserving config.json on upgrades: customize.sh backs it up, verify this works
- Forgetting to export variables: config.sh loads vars but must export for other scripts
- TUN mode permission errors: TUN device requires 0666 permissions and full root capabilities; using setuidgid will cause "permission denied" errors
Current version: v1.3.1 (versionCode: 20260118)
Recent major changes (v1.3.0+):
- Refactored to modular shell script architecture
- Added health check functionality
- Automatic log rotation
- Improved IPv6 support with parallel rule structures
- Enhanced error handling and user-friendly validation messages
- Automatic permission fixing on startup
- Graceful TUN fallback to tproxy mode