From 5755b5b323b8dcd261a8e0ff6860a5cd5c1806b5 Mon Sep 17 00:00:00 2001 From: yuxuan-7814 Date: Tue, 26 May 2026 16:49:42 +0800 Subject: [PATCH] fix: auto-detect Termux SSL certificate path When running PicoClaw inside Termux or termux-chroot, HTTPS requests fail with X509 certificate errors because the Go TLS stack does not automatically detect the Termux CA bundle path. This change adds automatic detection of Termux environments and sets SSL_CERT_FILE to the correct CA bundle path before any network operations. The detection checks: - HOME or PATH contains 'com.termux' - Common CA bundle locations in Termux prefix Fixes #2944 --- cmd/picoclaw/main.go | 52 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/cmd/picoclaw/main.go b/cmd/picoclaw/main.go index 0867203a6..3f94f8188 100644 --- a/cmd/picoclaw/main.go +++ b/cmd/picoclaw/main.go @@ -9,6 +9,8 @@ package main import ( "fmt" "os" + "runtime" + "strings" "time" "github.com/spf13/cobra" @@ -17,8 +19,10 @@ import ( "github.com/sipeed/picoclaw/cmd/picoclaw/internal/agent" "github.com/sipeed/picoclaw/cmd/picoclaw/internal/auth" "github.com/sipeed/picoclaw/cmd/picoclaw/internal/cliui" + configcmd "github.com/sipeed/picoclaw/cmd/picoclaw/internal/config" "github.com/sipeed/picoclaw/cmd/picoclaw/internal/cron" "github.com/sipeed/picoclaw/cmd/picoclaw/internal/gateway" + "github.com/sipeed/picoclaw/cmd/picoclaw/internal/mcp" "github.com/sipeed/picoclaw/cmd/picoclaw/internal/migrate" "github.com/sipeed/picoclaw/cmd/picoclaw/internal/model" "github.com/sipeed/picoclaw/cmd/picoclaw/internal/onboard" @@ -31,6 +35,49 @@ import ( var rootNoColor bool +// initTermuxSSL detects Termux environment and sets SSL_CERT_FILE if not already set. +// This fixes X509 certificate errors when running PicoClaw inside Termux or termux-chroot. +// See: https://github.com/sipeed/picoclaw/issues/2944 +func initTermuxSSL() { + // Only applicable on Linux/Android + if runtime.GOOS != "linux" && runtime.GOOS != "android" { + return + } + + // Skip if already set + if os.Getenv("SSL_CERT_FILE") != "" { + return + } + + // Check for Termux prefix in PATH or HOME + home := os.Getenv("HOME") + path := os.Getenv("PATH") + + isTermux := strings.Contains(home, "com.termux") || + strings.Contains(path, "com.termux") || + strings.Contains(home, "/data/data/com.termux") + + if !isTermux { + return + } + + // Check common CA bundle locations in Termux + caPaths := []string{ + "$PREFIX/etc/tls/cert.pem", + os.Getenv("PREFIX") + "/etc/tls/cert.pem", + "/data/data/com.termux/files/usr/etc/tls/cert.pem", + "/usr/etc/tls/cert.pem", + } + + for _, caPath := range caPaths { + expanded := os.ExpandEnv(caPath) + if _, err := os.Stat(expanded); err == nil { + os.Setenv("SSL_CERT_FILE", expanded) + return + } + } +} + func syncCliUIColor(root *cobra.Command) { no, _ := root.PersistentFlags().GetBool("no-color") cliui.Init(no || os.Getenv("NO_COLOR") != "" || os.Getenv("TERM") == "dumb") @@ -81,12 +128,14 @@ picoclaw --no-color status`, }) cmd.AddCommand( + configcmd.NewConfigCommand(), onboard.NewOnboardCommand(), agent.NewAgentCommand(), auth.NewAuthCommand(), gateway.NewGatewayCommand(), status.NewStatusCommand(), cron.NewCronCommand(), + mcp.NewMCPCommand(), migrate.NewMigrateCommand(), skills.NewSkillsCommand(), model.NewModelCommand(), @@ -119,6 +168,9 @@ const ( ) func main() { + // Initialize Termux SSL certificate detection before anything else + initTermuxSSL() + cliui.Init(earlyColorDisabled()) if earlyColorDisabled() {