INC-2026-03-16: Resolution

Resolution

Decision: Clean reinstall of Termux from F-Droid.

Reinstall Steps

  • Uninstall Termux (and plugins)

  • Install Termux from F-Droid (NOT Play Store)

  • pkg update && pkg upgrade

  • pkg install openssh git neovim rsync stow pass gnupg fish oh-my-posh termux-api

  • passwd / sshd / termux-setup-storage / whoami

  • Phase 2: SSH key transfer and Vault cert auth

  • Phase 3-4: Git and GPG/gopass setup

  • Phase 5-6: Repos and dotfiles sync (dotfiles-optimus, domus-nvim)

  • Phase 7: Prompt setup (Starship - oh-my-posh broken on ARM64)

  • Phase 8: Clipboard for gopass (Termux:API app from F-Droid)

  • Phase 9: Set zsh as default shell

  • Phase 10: Clone productivity repos (domus-captures, domus-infra-ops)

  • Verify: gopass ls, ssh -T git@github.com

  • Remove temporary password auth from SSH config

Temporary SSH Config Change

File: ~/.ssh/config

Change: Enable password auth temporarily for initial connection (before keys transferred)

Apply Change

# Add PasswordAuthentication to fold7 entry
sed -i '/^Host fold7$/,/UserKnownHostsFile/{s/UserKnownHostsFile.*/&\n    PasswordAuthentication yes\n    PreferredAuthentications publickey,password/}' ~/.ssh/config

# Verify
grep -A 15 "^Host fold7$" ~/.ssh/config

Update User (after running whoami on phone)

# Replace old user with new (example: u0_a385 -> u0_a123)
sed -i '/^Host fold7$/,/^Host [^*]/{s/User u0_a385/User <NEW_USER>/}' ~/.ssh/config

Expected Result

 Host fold7
     HostName 10.50.10.110
     Port 8022
-    User u0_a385
+    User <whoami output>
     IdentityFile ~/.ssh/id_ed25519_vault
     CertificateFile ~/.ssh/id_ed25519_vault-cert.pub
     IdentityFile ~/.ssh/id_ed25519_sk_rk_d000_nano
     IdentityFile ~/.ssh/id_ed25519_sk_rk_d000
     IdentityFile ~/.ssh/id_ed25519_sk_rk_d000_secondary
     IdentityFile ~/.ssh/id_ed25519_d000
     StrictHostKeyChecking no
     UserKnownHostsFile ~/.ssh/known_hosts_mobile
+    PasswordAuthentication yes
+    PreferredAuthentications publickey,password

Revert After Keys Transferred

# Remove temporary password auth
sed -i '/^Host fold7$/,/^Host [^*]/{/PasswordAuthentication yes/d; /PreferredAuthentications publickey,password/d}' ~/.ssh/config
Remove PasswordAuthentication yes after keys are transferred.

Phase 2: SSH Key Transfer and Vault Cert

Generate Keypair on Phone

On phone (Termux):

ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_vault -N "" -C "zfold7-termux"

Fetch Public Key to Workstation

Use sudo to bypass hardened SSH config (too many auth failures with 6 identity files).
# Copy public key from phone (use sudo due to SSH config)
sudo scp -o PubkeyAuthentication=no -P 8022 u0_a385@10.50.10.110:~/.ssh/id_ed25519_vault.pub /tmp/zfold7.pub

# Fix ownership (sudo created as root)
sudo chown evanusmodestus:evanusmodestus /tmp/zfold7.pub

Load Vault Credentials

dsource d000 dev/vault
vault status

Fix Vault Role: Add u0_a385 to allowed_users

Check current allowed_users (u0_a385 is NOT in list - only u0_a361 from old install):

vault read ssh/roles/domus-client -format=json | jq -r '.data.allowed_users | split(",") | .[]'

Get current allowed_users as comma-separated string for modification:

vault read ssh/roles/domus-client -format=json | jq -r '.data.allowed_users'

Update role to add u0_a385:

vault write ssh/roles/domus-client - <<'EOF'
{
  "allowed_users": "Administrator,domus\\Administrator,admin,adminerosado,ansible,evanusmodestus,gabriel,root,u0_a361,u0_a385",
  "key_type": "ca",
  "default_user": "evanusmodestus",
  "ttl": "8h",
  "allow_user_certificates": true,
  "default_extensions": {"permit-pty": "", "permit-user-rc": ""}
}
EOF

Verify u0_a385 is now in allowed_users:

vault read ssh/roles/domus-client -format=json | jq -r '.data.allowed_users | split(",") | .[] | select(test("u0_a"))'

Sign Certificate with Vault SSH CA

vault write -field=signed_key ssh/sign/domus-client \
  public_key=@/tmp/zfold7.pub \
  valid_principals="evanusmodestus,u0_a385" \
  > /tmp/zfold7-cert.pub

Verify certificate:

ssh-keygen -L -f /tmp/zfold7-cert.pub | awk '/Principals:/{p=1} p && /^ /{print; if(/^[^ ]/)p=0}'

Copy Cert Back to Phone

sudo scp -o PubkeyAuthentication=no -P 8022 /tmp/zfold7-cert.pub u0_a385@10.50.10.110:~/.ssh/id_ed25519_vault-cert.pub

Copy Other SSH Keys to Phone

# Software keys (transferable)
sudo scp -o PubkeyAuthentication=no -P 8022 \
  ~/.ssh/id_ed25519_d000 \
  ~/.ssh/id_ed25519_d000.pub \
  ~/.ssh/id_ed25519_github \
  ~/.ssh/id_ed25519_github.pub \
  ~/.ssh/id_ed25519_gitlab \
  ~/.ssh/id_ed25519_gitlab.pub \
  ~/.ssh/id_ed25519_gitea \
  ~/.ssh/id_ed25519_gitea.pub \
  ~/.ssh/id_ed25519_codeberg \
  ~/.ssh/id_ed25519_codeberg.pub \
  ~/.ssh/id_ed25519_bitbucket \
  ~/.ssh/id_ed25519_bitbucket.pub \
  ~/.ssh/config \
  u0_a385@10.50.10.110:~/.ssh/

# YubiKey keys (NOT transferable - hardware bound):
# - id_ed25519_sk_rk_d000_nano
# - id_ed25519_sk_rk_d000
# - id_ed25519_sk_rk_d000_secondary

Set Permissions on Phone

chmod 600 ~/.ssh/id_ed25519_*
chmod 644 ~/.ssh/*.pub ~/.ssh/*-cert.pub
chmod 600 ~/.ssh/config

Configure Phone authorized_keys for Cert Auth

Add workstation’s public key (fallback) and Vault CA (cert auth):

# From workstation - add d000 pubkey
cat ~/.ssh/id_ed25519_d000.pub | sudo ssh -o PubkeyAuthentication=no -p 8022 u0_a385@10.50.10.110 'cat >> ~/.ssh/authorized_keys'

# Add Vault CA for cert auth
vault read -field=public_key ssh/config/ca | awk '{print "cert-authority "$0}' | sudo ssh -o PubkeyAuthentication=no -p 8022 u0_a385@10.50.10.110 'cat >> ~/.ssh/authorized_keys'

Re-sign Workstation Cert with u0_a385 Principal

The workstation’s cert needs u0_a385 principal to authenticate to the phone:

vault write -field=signed_key ssh/sign/domus-client \
  public_key=@$HOME/.ssh/id_ed25519_vault.pub \
  valid_principals="Administrator,admin,adminerosado,ansible,domus\\Administrator,evanusmodestus,u0_a385" \
  >| ~/.ssh/id_ed25519_vault-cert.pub

# Verify u0_a385 is in principals
ssh-keygen -L -f ~/.ssh/id_ed25519_vault-cert.pub | grep -A10 Principals

Verify SSH Works with Vault Cert

# From workstation (uses id_ed25519_vault with cert)
ssh fold7

Phase 3-4: Git and GPG/gopass Setup

Create SSH Sockets Directory (Phone)

mkdir -p ~/.ssh/sockets

Verify GitHub SSH (Phone)

ssh -T git@github.com

Transfer GPG Key from Workstation

On workstation:

# Export secret key
gpg --export-secret-keys --armor 28A3183647525597 > /tmp/gpg-secret.asc

# Copy to phone
sudo scp -P 8022 /tmp/gpg-secret.asc u0_a385@10.50.10.110:~/

# Clean up
rm /tmp/gpg-secret.asc

Import GPG Key (Phone)

# Import
gpg --import ~/gpg-secret.asc

# Trust the key
gpg --edit-key 28A3183647525597
# Type: trust → 5 → y → quit

# Verify
gpg --list-secret-keys

# Clean up
rm ~/gpg-secret.asc

Clone gopass Store (Phone)

mkdir -p ~/.local/share/gopass/stores
git clone git@github.com:EvanusModestus/gopass-v3.git ~/.local/share/gopass/stores/v3

Configure GPG Agent for Termux (Phone)

mkdir -p ~/.gnupg
echo "pinentry-program /data/data/com.termux/files/usr/bin/pinentry-tty" > ~/.gnupg/gpg-agent.conf
echo "allow-loopback-pinentry" >> ~/.gnupg/gpg-agent.conf

# Restart gpg-agent
gpgconf --kill gpg-agent
gpg-agent --daemon

Configure gopass Mount (Phone)

Do NOT manually edit config file. Use gopass mounts add command.
# Add v3 store as mount
gopass mounts add v3 ~/.local/share/gopass/stores/v3

# Verify mount
gopass mounts

# Test
gopass ls
gopass show domains/d000/identity/ssh/github

Phase 5-6: Repos and Dotfiles Sync

Clone dotfiles-optimus (Phone)

mkdir -p ~/atelier/_projects/personal
git clone git@github.com:EvanusModestus/dotfiles-optimus.git ~/atelier/_projects/personal/dotfiles-optimus

Clone domus-nvim (Phone)

git clone git@github.com:EvanusModestus/domus-nvim.git ~/.config/nvim

blink.cmp requires Rust build for performance:

pkg install rust binutils make clang
cd ~/.local/share/nvim/lazy/blink.cmp
cargo build --release

Install LSPs via Termux (Phone)

Mason doesn’t work on Termux (no Android binaries). Install LSPs directly:

# Node.js for npm-based LSPs
pkg install nodejs-lts

# TypeScript/JavaScript
npm install -g typescript typescript-language-server

# Python
npm install -g pyright

# Bash
npm install -g bash-language-server

# YAML/JSON/HTML/CSS
npm install -g yaml-language-server vscode-langservers-extracted

# Rust
pkg install rust-analyzer

# C/C++
pkg install clang

Configure Neovim for Termux (domus-nvim)

File: ~/.config/nvim/lua/domus/plugins/config/lsp/init.lua

Add at top of file:

---@diagnostic disable: undefined-global

Wrap ensure_installed to skip on Termux (around line 48):

require("mason-lspconfig").setup({
    ensure_installed = vim.fn.isdirectory("/data/data/com.termux") == 1 and {} or {
        "lua_ls", "pyright", "rust_analyzer", "ts_ls",
        -- ... rest of servers
    },

Add Termux fallback after mason-lspconfig.setup() closes (before function end):

-- Termux fallback: setup LSPs using nvim 0.11 native API
if vim.fn.isdirectory("/data/data/com.termux") == 1 then
    vim.lsp.config.lua_ls = {
        cmd = { "lua-language-server" },
        filetypes = { "lua" },
        root_markers = { ".git", ".luarc.json", "init.lua" },
        settings = { Lua = { diagnostics = { globals = { "vim" } } } },
    }
    vim.lsp.config.pyright = {
        cmd = { "pyright-langserver", "--stdio" },
        filetypes = { "python" },
        root_markers = { ".git", "pyproject.toml", "setup.py" },
    }
    vim.lsp.config.rust_analyzer = {
        cmd = { "rust-analyzer" },
        filetypes = { "rust" },
        root_markers = { ".git", "Cargo.toml" },
    }
    vim.lsp.enable({ "lua_ls", "pyright", "rust_analyzer" })
end

File: ~/.config/nvim/lua/domus/plugins/specs/lang/markdown.lua

Disable obsidian.nvim on Termux:

{
    "epwalsh/obsidian.nvim",
    cond = vim.fn.isdirectory("/data/data/com.termux") == 0, -- disable on Termux
    -- ...
},
Mason errors on startup are expected until ensure_installed is wrapped. System-installed LSPs work via native fallback.

Stow Shell and Tool Configs (Phone)

Available in base/: fish, zsh, git, vim, fzf, fd, ripgrep, gpg

cd ~/atelier/_projects/personal/dotfiles-optimus/base

# Fish shell
stow -t ~ fish

# Zsh shell
stow -t ~ zsh

# Git config
stow -t ~ git

# Vim (fallback)
stow -t ~ vim

# Search tools
stow -t ~ fzf
stow -t ~ fd
stow -t ~ ripgrep

Phase 7: Prompt Setup (Phone)

Oh-My-Posh - BROKEN on ARM64 Termux

Oh-my-posh crashes on ARM64 Termux with Go runtime panics (writeSegmentsConcurrently race condition). All themes crash, including built-in minimal themes. disable_async = true does not fix it.

Do NOT use oh-my-posh on Termux.

Solution: Use Starship Instead

Starship is Rust-based and works reliably on ARM64.

# Install starship
curl -sS https://starship.rs/install.sh | sh -s -- --bin-dir ~/.local/bin

# Remove oh-my-posh if installed
rm -f ~/.local/bin/oh-my-posh
rm -rf ~/.cache/oh-my-posh

Configure Starship in .zshrc

Comment out oh-my-posh block and add starship:

# Comment out oh-my-posh (broken on ARM64)
sed -i '/^if command -v oh-my-posh/,/^fi$/s/^/#/' ~/.zshrc

# Add starship init
echo 'eval "$(starship init zsh)"' >> ~/.zshrc

Apply a Powerline Theme

# List available presets
starship preset --list

# Apply tokyo-night (matches workstation theme)
starship preset tokyo-night -o ~/.config/starship.toml

# Or catppuccin-powerline
starship preset catppuccin-powerline -o ~/.config/starship.toml

exec zsh

Expected Result

Powerline-style prompt with git status, path, and exit code:

󰌽 u0_a385 …/dotfiles-optimus/apps   main !   20:11  ❯

Phase 8: Clipboard for gopass (Phone)

gopass requires clipboard access for gopass show -c to work.

Install Termux:API App

You need BOTH the package AND the Android app.
  1. Package (CLI tools):

    pkg install termux-api
  2. Android App (handles API calls):

    Install from F-Droid: f-droid.org/packages/com.termux.api/

Without the Android app, clipboard commands hang forever.

Test Clipboard

echo "test" | termux-clipboard-set
termux-clipboard-get

Use with gopass

# Copy password to clipboard (clears after 45s)
gopass show -c domains/d000/identity/ssh/github

# Manual workaround if -c doesn't work
gopass show domains/d000/identity/ssh/github | head -1 | termux-clipboard-set

Phase 9: Set zsh as Default Shell

Bash is the default Termux shell. Change to zsh for consistency with workstation.

Change Default Shell (Phone)

# Set zsh as default
chsh -s zsh

# Verify
cat /data/data/com.termux/files/usr/etc/passwd | grep $(whoami)

Expected output:

u0_a385::10385:10385::/data/data/com.termux/files/home:/data/data/com.termux/files/usr/bin/zsh

Force zsh on Termux Start

If chsh doesn’t persist (some Termux versions), add to .bashrc:

echo 'exec zsh' >> ~/.bashrc

Verify (Close and Reopen Termux)

Terminal should show zsh prompt (starship) instead of bash $:

󰌽 u0_a385 ~   20:30  ❯

Phase 10: Mobile Productivity Repos

Clone repos to enable productive mobile workflow. Z Fold 7’s large screen makes AsciiDoc editing and studying practical.

Create Atelier Structure (Phone)

mkdir -p ~/atelier/_bibliotheca
mkdir -p ~/atelier/_projects/personal

Clone domus-captures (Phone)

Primary use: Regex training, worklogs, codex references.

git clone git@github.com:EvanusModestus/domus-captures.git ~/atelier/_bibliotheca/domus-captures

Clone domus-infra-ops (Phone)

Primary use: Runbook reference while on-call.

git clone git@github.com:EvanusModestus/domus-infra-ops.git ~/atelier/_bibliotheca/domus-infra-ops

Clone domus-netapi-docs (Phone)

Primary use: netapi CLI reference.

git clone git@github.com:EvanusModestus/domus-netapi-docs.git ~/atelier/_bibliotheca/domus-netapi-docs

Convenience Aliases

Add to ~/.zshrc or create in dotfiles:

# Quick nav aliases
alias dcap='cd ~/atelier/_bibliotheca/domus-captures'
alias dinfra='cd ~/atelier/_bibliotheca/domus-infra-ops'
alias dnetapi='cd ~/atelier/_bibliotheca/domus-netapi-docs'

# Today's worklog
alias wrklog='nvim ~/atelier/_bibliotheca/domus-captures/docs/modules/ROOT/pages/$(date +%Y/%m)/WRKLOG-$(date +%Y-%m-%d).adoc'

# Regex training
alias regex='cd ~/atelier/_bibliotheca/domus-captures/docs/modules/ROOT/pages/education/training/regex && nvim .'