karnel-termux 4.14.0 → 4.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/README.md +19 -7
  2. package/assets/fonts/font.ttf +0 -0
  3. package/docs/ARCHITECTURE.md +5 -0
  4. package/docs/CHANGELOG.md +23 -0
  5. package/docs/cli/README.md +97 -38
  6. package/install.sh +144 -63
  7. package/karnel/RELEASE_COMMIT +1 -0
  8. package/karnel/bin/karnel +49 -11
  9. package/karnel/cli/commands/backup.sh +165 -154
  10. package/karnel/cli/commands/env.sh +0 -3
  11. package/karnel/cli/commands/install.sh +8 -1
  12. package/karnel/cli/commands/list.sh +3 -0
  13. package/karnel/cli/commands/restore.sh +118 -77
  14. package/karnel/cli/commands/show.sh +8 -6
  15. package/karnel/cli/commands/start.sh +1 -0
  16. package/karnel/cli/commands/update.sh +2 -2
  17. package/karnel/cli/karnel.sh +27 -5
  18. package/karnel/modules/shell.sh +27 -10
  19. package/karnel/tools/ai/all.sh +28 -1
  20. package/karnel/tools/ai/ampcode/install.sh +63 -109
  21. package/karnel/tools/ai/cactus/README.md +9 -0
  22. package/karnel/tools/ai/cactus/install.sh +101 -4
  23. package/karnel/tools/ai/claude-code/install.sh +49 -22
  24. package/karnel/tools/ai/codebuff/install.sh +50 -118
  25. package/karnel/tools/ai/codegraph/install.sh +33 -23
  26. package/karnel/tools/ai/crush/install.sh +46 -10
  27. package/karnel/tools/ai/cursor-cli/install.sh +18 -28
  28. package/karnel/tools/ai/engram/install.sh +7 -4
  29. package/karnel/tools/ai/gentle-ai/install.sh +5 -25
  30. package/karnel/tools/ai/gentle-ai/termux-patches.go +0 -0
  31. package/karnel/tools/ai/gga/README.md +6 -6
  32. package/karnel/tools/ai/gga/install.sh +145 -101
  33. package/karnel/tools/ai/goose/install.sh +29 -8
  34. package/karnel/tools/ai/hermes-agent/install.sh +87 -49
  35. package/karnel/tools/ai/kimchi-code/install.sh +53 -90
  36. package/karnel/tools/ai/kiro/install.sh +102 -78
  37. package/karnel/tools/ai/mimocode/install.sh +45 -106
  38. package/karnel/tools/ai/odysseus/install.sh +28 -16
  39. package/karnel/tools/ai/opencode/install.sh +4 -0
  40. package/karnel/tools/ai/qoder/bin/qodercli +3 -2
  41. package/karnel/tools/ai/qoder/install.sh +108 -43
  42. package/karnel/tools/auto/all.sh +8 -7
  43. package/karnel/tools/auto/n8n/install.sh +1 -1
  44. package/karnel/tools/db/all.sh +8 -7
  45. package/karnel/tools/deploy/all.sh +8 -6
  46. package/karnel/tools/deploy/netlify/README.md +4 -0
  47. package/karnel/tools/deploy/supabase/install.sh +71 -33
  48. package/karnel/tools/dev/all.sh +13 -11
  49. package/karnel/tools/editor/all.sh +8 -7
  50. package/karnel/tools/editor/neovim/install.sh +1 -1
  51. package/karnel/tools/editor/nvchad/install.sh +86 -32
  52. package/karnel/tools/games/all.sh +8 -7
  53. package/karnel/tools/games/arcade/install.sh +2 -2
  54. package/karnel/tools/games/buzz/install.sh +2 -2
  55. package/karnel/tools/games/ctfgod/install.sh +2 -2
  56. package/karnel/tools/games/detective/install.sh +2 -2
  57. package/karnel/tools/games/pet-friends/install.sh +2 -2
  58. package/karnel/tools/games/tamagotchi/install.sh +2 -2
  59. package/karnel/tools/lang/all.sh +8 -7
  60. package/karnel/tools/network/all.sh +11 -13
  61. package/karnel/tools/network/dark/install.sh +1 -1
  62. package/karnel/tools/network/dedsec-network/install.sh +1 -1
  63. package/karnel/tools/npm/all.sh +8 -7
  64. package/karnel/tools/npm/live-server/install.sh +2 -1
  65. package/karnel/tools/npm/localtunnel/install.sh +2 -1
  66. package/karnel/tools/npm/markserv/install.sh +2 -1
  67. package/karnel/tools/npm/ncu/install.sh +2 -1
  68. package/karnel/tools/npm/nestjs/install.sh +2 -1
  69. package/karnel/tools/npm/ngrok/install.sh +2 -1
  70. package/karnel/tools/npm/prettier/install.sh +2 -1
  71. package/karnel/tools/npm/psqlformat/install.sh +2 -1
  72. package/karnel/tools/npm/turbopack/install.sh +20 -4
  73. package/karnel/tools/npm/typescript/install.sh +2 -1
  74. package/karnel/tools/npm/vercel/install.sh +2 -1
  75. package/karnel/tools/osint/all.sh +11 -13
  76. package/karnel/tools/plugins/install.sh +20 -27
  77. package/karnel/tools/security/aircrack-ng/install.sh +7 -2
  78. package/karnel/tools/security/all.sh +10 -4
  79. package/karnel/tools/security/amass/install.sh +22 -16
  80. package/karnel/tools/security/binwalk/install.sh +7 -2
  81. package/karnel/tools/security/burpsuite/install.sh +54 -26
  82. package/karnel/tools/security/dirb/install.sh +7 -2
  83. package/karnel/tools/security/dnsrecon/install.sh +7 -2
  84. package/karnel/tools/security/enum4linux/install.sh +7 -2
  85. package/karnel/tools/security/exiftool/install.sh +7 -2
  86. package/karnel/tools/security/ffuf/install.sh +18 -15
  87. package/karnel/tools/security/foremost/install.sh +7 -2
  88. package/karnel/tools/security/gobuster/install.sh +21 -17
  89. package/karnel/tools/security/hashcat/install.sh +7 -2
  90. package/karnel/tools/security/hydra/install.sh +7 -2
  91. package/karnel/tools/security/john/install.sh +7 -2
  92. package/karnel/tools/security/masscan/install.sh +6 -2
  93. package/karnel/tools/security/metasploit/install.sh +14 -8
  94. package/karnel/tools/security/netcat/install.sh +7 -2
  95. package/karnel/tools/security/nikto/install.sh +7 -2
  96. package/karnel/tools/security/nmap/install.sh +7 -2
  97. package/karnel/tools/security/smbclient/install.sh +7 -2
  98. package/karnel/tools/security/sqlmap/install.sh +7 -2
  99. package/karnel/tools/security/steghide/install.sh +7 -2
  100. package/karnel/tools/security/subfinder/install.sh +22 -16
  101. package/karnel/tools/security/tcpdump/install.sh +7 -2
  102. package/karnel/tools/security/theharvester/install.sh +7 -2
  103. package/karnel/tools/security/wafw00f/install.sh +42 -3
  104. package/karnel/tools/security/whatweb/install.sh +7 -2
  105. package/karnel/tools/security/whois/install.sh +7 -2
  106. package/karnel/tools/security/wpscan/install.sh +42 -3
  107. package/karnel/tools/security/zap/install.sh +44 -18
  108. package/karnel/tools/shell/all.sh +11 -10
  109. package/karnel/tools/shell/better-npm/install.sh +7 -4
  110. package/karnel/tools/shell/fzf-tab/install.sh +7 -4
  111. package/karnel/tools/shell/history-substring/install.sh +7 -4
  112. package/karnel/tools/shell/powerlevel10k/install.sh +7 -4
  113. package/karnel/tools/shell/you-should-use/install.sh +7 -4
  114. package/karnel/tools/shell/zsh-autopair/install.sh +7 -4
  115. package/karnel/tools/shell/zsh-autosuggestions/install.sh +7 -4
  116. package/karnel/tools/shell/zsh-completions/install.sh +7 -4
  117. package/karnel/tools/shell/zsh-defer/install.sh +7 -4
  118. package/karnel/tools/shell/zsh-syntax-highlighting/install.sh +7 -4
  119. package/karnel/tools/ui/all.sh +8 -7
  120. package/karnel/tools/utils/all.sh +13 -11
  121. package/karnel/tools/utils/applaunch/install.sh +2 -2
  122. package/karnel/tools/utils/fconv/install.sh +2 -2
  123. package/karnel/tools/utils/filecheck/install.sh +2 -2
  124. package/karnel/tools/utils/notes/install.sh +2 -2
  125. package/karnel/tools/utils/passman/install.sh +2 -2
  126. package/karnel/tools/utils/qrcode/install.sh +1 -1
  127. package/karnel/tools/utils/splash/install.sh +2 -2
  128. package/karnel/tools/utils/treex/install.sh +2 -2
  129. package/karnel/tools/utils/websites/install.sh +2 -2
  130. package/karnel/tools/voice/all.sh +1 -1
  131. package/karnel/utils/backup.sh +182 -0
  132. package/karnel/utils/bootstrap.sh +67 -3
  133. package/karnel/utils/downloaded-python.sh +50 -11
  134. package/karnel/utils/env.sh +27 -7
  135. package/karnel/utils/install.sh +272 -11
  136. package/karnel/utils/tools.sh +111 -6
  137. package/package.json +4 -3
  138. package/scripts/check-package.js +86 -9
  139. package/scripts/completion.bash +134 -13
  140. package/scripts/completion.zsh +138 -53
  141. package/scripts/npm-install.js +9 -3
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  <p align="center">
10
10
  <a href="https://github.com/israelmarques1024-dotcom/karnel-termux">
11
- <img src="https://img.shields.io/badge/version-4.14.0-0078D4?style=for-the-badge" alt="Version">
11
+ <img src="https://img.shields.io/badge/version-4.15.0-0078D4?style=for-the-badge" alt="Version">
12
12
  </a>
13
13
  <a href="https://www.npmjs.com/package/karnel-termux">
14
14
  <img src="https://img.shields.io/npm/v/karnel-termux?style=for-the-badge&logo=npm&color=cb3837" alt="npm">
@@ -56,10 +56,15 @@ Created by **Israel Marques**.
56
56
 
57
57
  ## Installation
58
58
 
59
- ### Via curl (recommended)
59
+ ### Via checksummed GitHub release (recommended)
60
60
 
61
61
  ```bash
62
- bash -c "$(curl -fsSL https://raw.githubusercontent.com/israelmarques1024-dotcom/karnel-termux/main/install.sh)"
62
+ version=4.15.0
63
+ tmpdir=$(mktemp -d) && trap 'rm -rf "$tmpdir"' EXIT
64
+ base="https://github.com/israelmarques1024-dotcom/karnel-termux/releases/download/v$version"
65
+ curl -fsSL "$base/karnel-termux-install.sh" -o "$tmpdir/karnel-termux-install.sh"
66
+ curl -fsSL "$base/karnel-termux-install.sh.sha256" -o "$tmpdir/karnel-termux-install.sh.sha256"
67
+ (cd "$tmpdir" && sha256sum -c karnel-termux-install.sh.sha256) && bash "$tmpdir/karnel-termux-install.sh" --ref "v$version"
63
68
  ```
64
69
 
65
70
  ### Via npm
@@ -80,6 +85,10 @@ After installation, run:
80
85
  karnel
81
86
  ```
82
87
 
88
+ In a terminal, running `karnel` without a command opens a curated menu for
89
+ common workflows. The menu is not a complete inventory of CLI commands,
90
+ aliases, or options; use `karnel help` and the CLI reference for the full set.
91
+
83
92
  ---
84
93
 
85
94
  ## Usage
@@ -104,7 +113,7 @@ karnel
104
113
  | `karnel start editor` | Start code-server (VS Code in browser) |
105
114
  | `karnel pg` | PostgreSQL manager |
106
115
  | `karnel init <template>` | Initialize projects with templates |
107
- | `karnel deploy` | Deploy projects (Vercel, Railway, Netlify, Supabase) |
116
+ | `karnel deploy` | Run Vercel, Railway, Netlify, or Supabase CLI commands |
108
117
  | `karnel supabase` | Manage the Supabase CLI and remote-project helpers |
109
118
  | `karnel robin` | Manage the Robin OSINT service |
110
119
  | `karnel --version` | Show installed version |
@@ -363,7 +372,9 @@ karnel brain sync # Sync with private GitHub
363
372
 
364
373
  ## karnel voice
365
374
 
366
- Capture audio from microphone, review in editor, copy to clipboard, and dispatch any AI agent with the transcribed prompt.
375
+ Capture audio from the microphone, optionally review it in an editor, copy it
376
+ to the clipboard, and dispatch an AI agent with the transcribed prompt. If an
377
+ editor is unavailable or no TTY is attached, Karnel uses the raw transcript.
367
378
 
368
379
  ```bash
369
380
  karnel voice opencode # Record -> edit -> opencode run
@@ -415,7 +426,7 @@ Microphone -> termux-speech-to-text -> editor (edit) -> clipboard -> AI agent
415
426
 
416
427
  - Termux:API: `pkg install termux-api`
417
428
  - App Termux:API: https://karneltermux.vercel.app/termux/api
418
- - Editor: `karnel install editor`
429
+ - Editor (optional): `karnel install editor`; otherwise use `--raw` or the automatic raw fallback
419
430
 
420
431
  ---
421
432
 
@@ -467,7 +478,8 @@ karnel env ls # List variables
467
478
 
468
479
  ## karnel deploy
469
480
 
470
- Deploy your projects directly from terminal:
481
+ Run an installed platform CLI through Karnel; arguments after the tool name are
482
+ forwarded unchanged:
471
483
 
472
484
  ```bash
473
485
  karnel deploy vercel # Deploy to Vercel
Binary file
@@ -83,6 +83,11 @@ import "@/cli/commands/doctor/code"
83
83
 
84
84
  The `@` symbol is replaced with `$KARNEL_PATH`. This avoids hardcoded paths.
85
85
 
86
+ `import()` is not an idempotent module loader: it calls `source` on every import
87
+ and does not track files already loaded. Top-level assignments and other side
88
+ effects therefore run again when the same file is imported more than once;
89
+ imported files must tolerate repeated evaluation where callers can repeat them.
90
+
86
91
  ### Critical Scope Implication
87
92
 
88
93
  Because `import()` calls `source` from within a function, any `declare` statement
package/docs/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # Documentation Changelog
2
2
 
3
+ ## 4.15.0 - 2026-08-14
4
+
5
+ - Replaced the unavailable GGA fork with the official Gentleman Guardian
6
+ Angel v2.10.1, pinned by commit, with Termux-safe staging, ownership
7
+ verification, rollback, and non-destructive uninstall.
8
+ - Made backup/restore transactional: staged configs, ancestor symlink
9
+ rejection, signal-safe rollback, concurrent backup isolation, validated
10
+ snapshot names, and explicit package/configuration phase separation.
11
+ - Hardened lifecycle ownership so pre-existing installs are never adopted,
12
+ arbitrary handler exit codes are treated as failures, and stale or empty
13
+ locks are recovered safely.
14
+ - Pinned release/npm/installer together: the npm package carries a validated
15
+ `RELEASE_COMMIT`, the release workflow is serialized per tag and refuses
16
+ divergent partial publications, and the installer activates only a verified,
17
+ clean checkout.
18
+ - Authenticated security and AI installers with fixed versions and official
19
+ SHA-256 checksums, safe archive extraction, staging, and rollback; unverifiable
20
+ upstream assets now fail closed instead of executing unauthenticated bytes.
21
+ - Fixed the npm postinstall to distinguish the npm bin symlink from a complete
22
+ managed installation, and removed the deprecated `curl | bash` guidance.
23
+ - Updated ShellCheck/syntax/test gates to 294 scripts and added regression
24
+ suites for backup, lifecycle, installer integrity, and AI installer hardening.
25
+
3
26
  ## 4.14.0 - 2026-08-14
4
27
 
5
28
  - Added tested Cactus 2.0.1 support on ARM64 through Ubuntu Proot with an
@@ -4,8 +4,9 @@
4
4
  karnel <command> [arguments]
5
5
  ```
6
6
 
7
- With no command, Karnel opens its interactive interface (TUI) when a terminal is
8
- available and prints help in noninteractive contexts.
7
+ With no command, Karnel opens a curated interactive menu (TUI) when a terminal
8
+ is available and prints help in noninteractive contexts. The menu exposes common
9
+ workflows, not every command, alias, or option documented below.
9
10
 
10
11
  ---
11
12
 
@@ -442,8 +443,9 @@ karnel env ls
442
443
  karnel voice [agent] [options]
443
444
  ```
444
445
 
445
- Captures audio via `termux-speech-to-text`, opens the transcript in `$EDITOR`,
446
- copies to clipboard, and dispatches the AI agent.
446
+ Captures audio via `termux-speech-to-text`, optionally opens the transcript in
447
+ `$EDITOR`, copies it to the clipboard, and dispatches the AI agent. If no editor
448
+ is installed or no TTY is available, Karnel continues with the raw transcript.
447
449
 
448
450
  ### Agent targets
449
451
 
@@ -539,18 +541,21 @@ cd . && karnel init # Auto-detect
539
541
 
540
542
  ---
541
543
 
542
- ## deploy — Deploy projects
544
+ ## deploy — Run deployment CLIs
543
545
 
544
546
  ```bash
545
547
  karnel deploy <tool> [args...]
546
548
  ```
547
549
 
548
- | Tool | Platform |
549
- |------|----------|
550
- | `vercel` | Vercel (passes remaining args to `vercel`) |
551
- | `railway` | Railway |
552
- | `netlify` | Netlify |
553
- | `supabase` | Supabase |
550
+ `deploy` checks that the selected executable exists, then replaces Karnel with
551
+ that CLI and forwards all remaining arguments.
552
+
553
+ | Tool | Executable |
554
+ |------|------------|
555
+ | `vercel` | `vercel` |
556
+ | `railway` | `railway` |
557
+ | `netlify` | `netlify` |
558
+ | `supabase` | `supabase`; this is a generic CLI pass-through, not a deployment action by itself |
554
559
 
555
560
  ```bash
556
561
  karnel deploy vercel
@@ -568,19 +573,19 @@ karnel deploy supabase
568
573
  karnel supabase <subcommand> [options]
569
574
  ```
570
575
 
571
- The top-level command manages Supabase CLI installation and remote-project
572
- workflows. It does not start the local Supabase stack on Termux; that requires
573
- Docker on a Linux host.
576
+ The top-level command manages the pinned Supabase CLI installation and provides
577
+ thin wrappers for linked-project workflows. It does not start the local Supabase
578
+ stack on Termux; `supabase start` requires Docker on a Linux host.
574
579
 
575
580
  | Subcommand | Description |
576
581
  |------------|-------------|
577
- | `doctor` | Check CLI, project configuration, and API reachability |
578
- | `types` | Generate TypeScript types for a linked project |
579
- | `migrate` | Run `supabase db <args>` |
580
- | `link` | Run `supabase link <args>` |
581
- | `remote-start` / `remote` | Show the remote-development guide |
582
- | `remote-status` / `status` | Check Supabase status |
583
- | `install` / `uninstall` | Install or remove the Supabase CLI |
582
+ | `doctor` | Check the CLI, `supabase/config.toml`, Docker expectations, and API reachability |
583
+ | `types` | Run `supabase gen types typescript --linked`; extra arguments are not forwarded |
584
+ | `migrate` | Forward arguments to `supabase db` |
585
+ | `link` | Forward arguments to `supabase link` |
586
+ | `remote-start` / `remote` | Print a guide for running the Docker-backed local stack on a Linux host |
587
+ | `remote-status` / `status` | Run `supabase status`, which reports the local stack rather than hosted-project health |
588
+ | `install` / `uninstall` | Install or remove Karnel's pinned Supabase CLI binary |
584
589
 
585
590
  ```bash
586
591
  karnel supabase install
@@ -589,6 +594,43 @@ karnel supabase types
589
594
  karnel supabase migrate push
590
595
  ```
591
596
 
597
+ The downloaded Linux binary may still fail on Android because of unsupported
598
+ system calls. The wrapper reports a `SIGSYS` failure and recommends PRoot or a
599
+ Linux host; installation alone does not guarantee native Android compatibility.
600
+
601
+ ---
602
+
603
+ ## plugin — Plugin manager
604
+
605
+ ```bash
606
+ karnel plugin <subcommand> [options]
607
+ ```
608
+
609
+ | Subcommand | Description |
610
+ |------------|-------------|
611
+ | `search [query]` | Search the pinned approved registry snapshot |
612
+ | `install <name>` | Install a plugin from that registry |
613
+ | `install <owner/repo> --unsafe` | Install an unreviewed repository after confirmation |
614
+ | `update <name>` | Atomically update an installed plugin |
615
+ | `remove` / `uninstall <name>` | Remove an installed plugin |
616
+ | `list` / `ls` | List installed plugins and their trust source |
617
+ | `create` / `scaffold <name>` | Create and activate a validated local plugin scaffold |
618
+
619
+ Search accepts `--command <name>`, `--compatible`, and `--capability <name>`.
620
+ Capabilities are `network`, `filesystem-read`, `filesystem-write`, `process`,
621
+ and `environment`. `--unsafe` applies only to `install` and `update`.
622
+
623
+ Plugins are Bash code, run with the current user's permissions, and are not
624
+ sandboxed. Registry review and manifest validation are not isolation boundaries.
625
+
626
+ ```bash
627
+ karnel plugin search backup --compatible
628
+ karnel plugin install karnel-hello
629
+ karnel plugin install owner/repo --unsafe
630
+ karnel plugin update karnel-hello
631
+ karnel plugin remove karnel-hello
632
+ ```
633
+
592
634
  ---
593
635
 
594
636
  ## pg — PostgreSQL manager
@@ -659,23 +701,29 @@ karnel backup restore [file]
659
701
  ```
660
702
 
661
703
  Creates `$KARNEL_DATA/backups/termux-<timestamp>.tar.gz` with SHA256 checksum.
704
+ Concurrent runs reserve distinct names atomically instead of overwriting a
705
+ backup created in the same second.
662
706
 
663
707
  ### Included in backup
664
708
 
665
- - All packages (`dpkg --get-selections`)
666
- - Karnel tool manifest
667
- - Shell configs (`.bashrc`, `.zshrc`, `.profile`)
709
+ - Package selections embedded under `metadata/packages.list` (`dpkg --get-selections`)
710
+ - A catalog snapshot of tool installer directories, not a record of which tools are installed
711
+ - Selected shell configs (`.bashrc`, `.zshrc`, `.profile`, `.zshenv`, `.inputrc`)
668
712
  - Termux configs (fonts, colors, properties)
669
- - SSH keys
670
- - `~/.config` app configs
671
- - APT repositories
713
+ - SSH configuration, known hosts, authorized keys, and public keys; private keys are excluded
714
+ - Selected `~/.config` application directories; `github-copilot`, `nvm`, `coc`, `Code`, and `yarn` are excluded, as are symbolic links and all `.env`, credential, auth, and token files matched by the backup filter
715
+ - `$PREFIX/etc/apt/sources.list` only; files under `sources.list.d` and APT keys are not included
716
+
717
+ Backups are gzip-compressed, not encrypted. API environment variables and SSH
718
+ private keys are not exported, but copied application configuration can still
719
+ contain credentials. Protect local and cloud copies as sensitive plaintext data.
672
720
 
673
721
  ### Options
674
722
 
675
723
  | Flag | Description |
676
724
  |------|-------------|
677
- | `--cloud` | Upload via `rclone` (remote named `karnel`) |
678
- | `snapshot <name>` | Create a named snapshot |
725
+ | `--cloud` | Legacy plaintext upload; requires explicit `KARNEL_ALLOW_PLAINTEXT_CLOUD_BACKUP=1` acknowledgement |
726
+ | `snapshot <name>` | Create a timestamped named snapshot using the same contents and secret filter as a full backup; names accept 1-64 letters, numbers, dots, underscores, or hyphens |
679
727
  | `list` / `ls` | List full backups and snapshots |
680
728
  | `info` / `show [file]` | Show a backup's contents (latest if omitted) |
681
729
  | `--cron` | Schedule a daily backup at 3:00 AM |
@@ -689,6 +737,11 @@ karnel backup snapshot before-update
689
737
  karnel backup list
690
738
  ```
691
739
 
740
+ Cloud backup is disabled by default because the archive is not encrypted or
741
+ signed. The legacy opt-in uploads both archive and checksum, but they share the
742
+ same remote trust boundary. Package selections and the informational tool
743
+ catalog are embedded in the archive.
744
+
692
745
  ---
693
746
 
694
747
  ## restore — Full Termux restore
@@ -699,22 +752,28 @@ karnel restore [--cloud] [--list] [<file>]
699
752
 
700
753
  ### What it restores
701
754
 
702
- - Shell configs to `~/`
755
+ - Selected shell configs to `~/`
703
756
  - Termux configs
704
- - SSH keys
705
- - `~/.config` directory
706
- - APT sources
707
- - Package list (via `dpkg --set-selections` + `apt-get dselect-upgrade`)
708
- - Karnel tools (from manifest)
709
-
710
- Verifies SHA256 checksum automatically if available.
757
+ - SSH configuration and public keys (legacy archives may also contain private keys)
758
+ - The selected `.config` directories present in the archive
759
+ - `$PREFIX/etc/apt/sources.list` when present
760
+ - Embedded package selections (via `dpkg --set-selections` + `apt-get dselect-upgrade`)
761
+ - The tool catalog is retained as metadata only; Karnel does not incorrectly reinstall every tool in the source catalog
762
+
763
+ Requires and verifies the SHA256 checksum before extraction. Restore rejects
764
+ archives containing traversal paths, links, devices, or other unsafe entries.
765
+ Configuration is first merged into staging so unrelated existing files remain
766
+ in place, then committed with local backups. A copy, move, or package-restore
767
+ failure rolls configuration back instead of reporting a partial success.
768
+ Package-manager operations themselves are not transactionally reversible by
769
+ Karnel if `apt-get` changes package state before returning an error.
711
770
 
712
771
  ### Options
713
772
 
714
773
  | Argument | Description |
715
774
  |----------|-------------|
716
775
  | `<file>` | Path to a specific backup file |
717
- | `--cloud` | Download and restore via the `karnel` rclone remote |
776
+ | `--cloud` | Legacy unauthenticated cloud restore; requires `KARNEL_ALLOW_UNAUTHENTICATED_CLOUD_RESTORE=1` |
718
777
  | `--list`, `-l` | List available full backups and snapshots |
719
778
  | `--help`, `-h` | Show help |
720
779
 
package/install.sh CHANGED
@@ -14,12 +14,17 @@ readonly P_NC='\e[0m'
14
14
  REPO="https://github.com/israelmarques1024-dotcom/karnel-termux"
15
15
  BRANCH="main"
16
16
  RELEASE_REF=""
17
+ INSTALL_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
18
+ RELEASE_COMMIT="${KARNEL_RELEASE_COMMIT:-}"
19
+ if [[ -z "$RELEASE_COMMIT" && -f "$INSTALL_SCRIPT_DIR/karnel/RELEASE_COMMIT" ]]; then
20
+ read -r RELEASE_COMMIT <"$INSTALL_SCRIPT_DIR/karnel/RELEASE_COMMIT" || RELEASE_COMMIT=""
21
+ fi
17
22
  KARNEL_DATA="${XDG_DATA_HOME:-$HOME/.local/share}/karnel-data"
18
23
  KARNEL_REPO="${XDG_DATA_HOME:-$HOME/.local/share}/karnel"
19
24
  KARNEL_CACHE="${XDG_CACHE_HOME:-$HOME/.cache}/karnel"
20
25
  KARNEL_CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}/karnel"
21
26
 
22
- TOTAL_STEPS=6
27
+ TOTAL_STEPS=4
23
28
  CURRENT_STEP=0
24
29
 
25
30
  _cols() {
@@ -66,26 +71,46 @@ log_info() {
66
71
  echo -e " ${P_BORDER}→${P_NC} $1"
67
72
  }
68
73
 
69
- _INSTALL_CREATED_KARNEL_REPO=false
70
- _INSTALL_CREATED_KARNEL_SYMLINK=false
74
+ _INSTALL_STAGING_ROOT=""
75
+ _INSTALL_PREVIOUS_KARNEL_REPO=""
76
+ _INSTALL_REPO_ACTIVATED=false
77
+ _INSTALL_REPO_HAD_PREVIOUS=false
78
+ _INSTALL_KARNEL_SYMLINK_CHANGED=false
71
79
  _INSTALL_PREVIOUS_KARNEL_SYMLINK=""
72
80
 
73
81
  _cleanup_failed() {
74
82
  echo -e "\n ${P_FAIL}✖${P_NC} Installation failed at step ${CURRENT_STEP}. Cleaning up..."
75
- if $_INSTALL_CREATED_KARNEL_REPO && [[ -d "$KARNEL_REPO" ]]; then
76
- rm -rf "$KARNEL_REPO"
77
- fi
78
- if $_INSTALL_CREATED_KARNEL_SYMLINK && [[ -L "$PREFIX/bin/karnel" ]]; then
83
+ if $_INSTALL_KARNEL_SYMLINK_CHANGED; then
79
84
  if [[ -n "$_INSTALL_PREVIOUS_KARNEL_SYMLINK" ]]; then
80
- ln -sfn "$_INSTALL_PREVIOUS_KARNEL_SYMLINK" "$PREFIX/bin/karnel"
85
+ local rollback_link_dir
86
+ rollback_link_dir=$(mktemp -d "$PREFIX/bin/.karnel-rollback.XXXXXX")
87
+ ln -s "$_INSTALL_PREVIOUS_KARNEL_SYMLINK" "$rollback_link_dir/karnel"
88
+ mv -Tf "$rollback_link_dir/karnel" "$PREFIX/bin/karnel"
89
+ rmdir "$rollback_link_dir"
81
90
  else
82
91
  rm -f "$PREFIX/bin/karnel"
83
92
  fi
84
93
  fi
94
+ if $_INSTALL_REPO_ACTIVATED; then
95
+ rm -rf "$KARNEL_REPO"
96
+ if $_INSTALL_REPO_HAD_PREVIOUS && [[ -d "$_INSTALL_PREVIOUS_KARNEL_REPO" ]]; then
97
+ mv "$_INSTALL_PREVIOUS_KARNEL_REPO" "$KARNEL_REPO"
98
+ fi
99
+ fi
100
+ [[ -z "$_INSTALL_STAGING_ROOT" ]] || rm -rf "$_INSTALL_STAGING_ROOT"
85
101
  echo -e " ${P_DIM}Run install.sh again to retry${P_NC}"
86
102
  exit 1
87
103
  }
88
104
 
105
+ _finish_install() {
106
+ if $_INSTALL_REPO_HAD_PREVIOUS && [[ -d "$_INSTALL_PREVIOUS_KARNEL_REPO" ]]; then
107
+ rm -rf "$_INSTALL_PREVIOUS_KARNEL_REPO"
108
+ fi
109
+ [[ -z "$_INSTALL_STAGING_ROOT" ]] || rm -rf "$_INSTALL_STAGING_ROOT"
110
+ _INSTALL_REPO_ACTIVATED=false
111
+ _INSTALL_KARNEL_SYMLINK_CHANGED=false
112
+ }
113
+
89
114
  separator() {
90
115
  local cols=$(_cols)
91
116
  local line=$(printf "%${cols}s")
@@ -178,7 +203,21 @@ install_dependencies() {
178
203
  setup_directories() {
179
204
  log_step 2 "Setting up directories"
180
205
 
181
- mkdir -p "$KARNEL_REPO" "$KARNEL_DATA" "$KARNEL_CACHE" "$KARNEL_CONFIG"
206
+ umask 077
207
+ local directory
208
+ for directory in "$KARNEL_DATA" "$KARNEL_CACHE" "$KARNEL_CONFIG"; do
209
+ if [[ -L "$directory" ]]; then
210
+ log_fail "Refusing symlink directory: $directory"
211
+ return 1
212
+ fi
213
+ mkdir -p -m 700 "$directory" || return 1
214
+ chmod 700 "$directory" || return 1
215
+ done
216
+ if [[ -L "$KARNEL_REPO" ]]; then
217
+ log_fail "Refusing symlink directory: $KARNEL_REPO"
218
+ return 1
219
+ fi
220
+ mkdir -p -m 700 "$(dirname "$KARNEL_REPO")" || return 1
182
221
 
183
222
  log_info "Repo $KARNEL_REPO"
184
223
  log_info "Data $KARNEL_DATA"
@@ -190,11 +229,10 @@ setup_directories() {
190
229
  clone_repo() {
191
230
  log_step 3 "Cloning repository"
192
231
 
193
- local script_dir
194
- script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
232
+ local script_dir="$INSTALL_SCRIPT_DIR"
195
233
  local is_dev_install=0
196
234
 
197
- if [[ -d "$script_dir/.git" ]] && [[ "$script_dir" != "$KARNEL_REPO" ]]; then
235
+ if [[ "$script_dir" != "$KARNEL_REPO" ]] && git -C "$script_dir" rev-parse --is-inside-work-tree &>/dev/null; then
198
236
  is_dev_install=1
199
237
  fi
200
238
 
@@ -206,32 +244,38 @@ clone_repo() {
206
244
  KARNEL_REPO="$script_dir"
207
245
  log_info "Developer installation detected"
208
246
  log_ok "Using local repository"
209
- elif [[ -d "$KARNEL_REPO/.git" ]]; then
247
+ else
248
+ local existing_repo=0
249
+ if [[ -e "$KARNEL_REPO" ]]; then
250
+ if ! git -C "$KARNEL_REPO" rev-parse --is-inside-work-tree &>/dev/null; then
251
+ log_fail "Refusing to replace pre-existing non-repository: $KARNEL_REPO"
252
+ return 1
253
+ fi
254
+ existing_repo=1
255
+ fi
256
+
257
+ if [[ $existing_repo -eq 1 ]]; then
210
258
  local origin_url
211
259
  origin_url="$(git -C "$KARNEL_REPO" remote get-url origin 2>/dev/null)"
212
- if [[ "$origin_url" != "$REPO" && "$origin_url" != "${REPO%.git}" ]]; then
260
+ if [[ "${origin_url%.git}" != "${REPO%.git}" ]]; then
213
261
  log_fail "Existing Karnel repository has an unexpected origin"
214
262
  return 1
215
263
  fi
216
- progress_bar 3 10
217
- if [[ -n "$RELEASE_REF" ]]; then
218
- if ! git -C "$KARNEL_REPO" fetch --depth=1 origin "refs/tags/$BRANCH" &>/dev/null ||
219
- ! git -C "$KARNEL_REPO" checkout --detach FETCH_HEAD &>/dev/null; then
220
- log_fail "Failed to update existing repository"
221
- return 1
222
- fi
223
- else
224
- if ! git -c core.hooksPath=/dev/null -C "$KARNEL_REPO" pull origin "$BRANCH" &>/dev/null; then
225
- log_fail "Failed to update existing repository"
226
- return 1
227
- fi
264
+ local worktree_status
265
+ if ! worktree_status=$(git -C "$KARNEL_REPO" status --porcelain --untracked-files=all); then
266
+ log_fail "Failed to inspect existing Karnel repository"
267
+ return 1
228
268
  fi
229
- progress_bar 10 10
230
- echo
231
- log_ok "Repository updated"
232
- else
269
+ if [[ -n "$worktree_status" ]]; then
270
+ log_fail "Refusing to update a dirty Karnel repository"
271
+ return 1
272
+ fi
273
+ fi
274
+
275
+ _INSTALL_STAGING_ROOT=$(mktemp -d "$(dirname "$KARNEL_REPO")/.karnel-staging.XXXXXX")
276
+ local candidate_repo="$_INSTALL_STAGING_ROOT/repo"
233
277
  progress_bar 0 10
234
- git clone --depth=1 -b "$BRANCH" "$REPO" "$KARNEL_REPO" &>/dev/null &
278
+ git clone --depth=1 --branch "$BRANCH" "$REPO" "$candidate_repo" &>/dev/null &
235
279
  local pid=$!
236
280
  local dots=0
237
281
  while kill -0 "$pid" 2>/dev/null; do
@@ -239,11 +283,42 @@ clone_repo() {
239
283
  printf "\r Cloning%s " "$(printf '%*s' "$dots" '' | tr ' ' '.')"
240
284
  sleep 0.5
241
285
  done
242
- wait "$pid" || { log_fail "Failed to clone repository"; _cleanup_failed; exit 1; }
243
- _INSTALL_CREATED_KARNEL_REPO=true
286
+ if ! wait "$pid"; then
287
+ log_fail "Failed to clone repository"
288
+ return 1
289
+ fi
290
+ if [[ -n "$RELEASE_REF" ]]; then
291
+ local candidate_commit
292
+ candidate_commit=$(git -C "$candidate_repo" rev-parse HEAD 2>/dev/null) || return 1
293
+ if [[ "$candidate_commit" != "$RELEASE_COMMIT" ]]; then
294
+ log_fail "Release commit mismatch for $RELEASE_REF"
295
+ log_info "Expected $RELEASE_COMMIT, got $candidate_commit"
296
+ return 1
297
+ fi
298
+ fi
299
+
300
+ if [[ $existing_repo -eq 1 ]]; then
301
+ _INSTALL_PREVIOUS_KARNEL_REPO=$(mktemp -d "$(dirname "$KARNEL_REPO")/.karnel-previous.XXXXXX")
302
+ rmdir "$_INSTALL_PREVIOUS_KARNEL_REPO"
303
+ mv "$KARNEL_REPO" "$_INSTALL_PREVIOUS_KARNEL_REPO"
304
+ _INSTALL_REPO_HAD_PREVIOUS=true
305
+ fi
306
+ if ! mv "$candidate_repo" "$KARNEL_REPO"; then
307
+ if $_INSTALL_REPO_HAD_PREVIOUS; then
308
+ mv "$_INSTALL_PREVIOUS_KARNEL_REPO" "$KARNEL_REPO"
309
+ _INSTALL_REPO_HAD_PREVIOUS=false
310
+ fi
311
+ log_fail "Failed to activate repository"
312
+ return 1
313
+ fi
314
+ _INSTALL_REPO_ACTIVATED=true
244
315
  progress_bar 10 10
245
316
  echo
246
- log_ok "Repository cloned"
317
+ if [[ $existing_repo -eq 1 ]]; then
318
+ log_ok "Repository updated"
319
+ else
320
+ log_ok "Repository cloned"
321
+ fi
247
322
  fi
248
323
 
249
324
  export KARNEL_REPO
@@ -263,20 +338,21 @@ create_symlink() {
263
338
 
264
339
  [[ -z "$PREFIX" ]] && { log_fail "PREFIX is not set. Are you running in Termux?"; return 1; }
265
340
 
266
- command -v termux-fix-shebang &>/dev/null && termux-fix-shebang "$KARNEL_REPO/karnel/bin/karnel" &>/dev/null
267
-
268
- if [[ -e "$PREFIX/bin/karnel" && ! -L "$PREFIX/bin/karnel" ]]; then
341
+ if [[ ( -e "$PREFIX/bin/karnel" || -L "$PREFIX/bin/karnel" ) && ! -L "$PREFIX/bin/karnel" ]]; then
269
342
  log_fail "Refusing to replace existing non-symlink: $PREFIX/bin/karnel"
270
343
  return 1
271
344
  fi
272
345
  if [[ -L "$PREFIX/bin/karnel" ]]; then
273
346
  _INSTALL_PREVIOUS_KARNEL_SYMLINK="$(readlink "$PREFIX/bin/karnel")"
274
347
  fi
275
- rm -f "$PREFIX/bin/karnel"
276
- ln -sf "$KARNEL_REPO/karnel/bin/karnel" "$PREFIX/bin/karnel"
348
+ local link_staging
349
+ link_staging=$(mktemp -d "$PREFIX/bin/.karnel-link.XXXXXX")
350
+ ln -s "$KARNEL_REPO/karnel/bin/karnel" "$link_staging/karnel"
351
+ mv -Tf "$link_staging/karnel" "$PREFIX/bin/karnel"
352
+ rmdir "$link_staging"
353
+ _INSTALL_KARNEL_SYMLINK_CHANGED=true
277
354
 
278
355
  if [[ -L "$PREFIX/bin/karnel" ]]; then
279
- _INSTALL_CREATED_KARNEL_SYMLINK=true
280
356
  log_ok "Symlink created: karnel → ${KARNEL_REPO}/karnel/bin/karnel"
281
357
  else
282
358
  log_fail "Failed to create symlink"
@@ -284,19 +360,6 @@ create_symlink() {
284
360
  fi
285
361
  }
286
362
 
287
- save_config() {
288
- log_step 5 "Saving configuration"
289
-
290
- cat >"$KARNEL_CONFIG/config" <<EOF
291
- karnel_repo='$KARNEL_REPO'
292
- karnel_data='$KARNEL_DATA'
293
- karnel_cache='$KARNEL_CACHE'
294
- karnel_config='$KARNEL_CONFIG'
295
- EOF
296
-
297
- log_ok "Configuration saved"
298
- }
299
-
300
363
  show_final_message() {
301
364
  echo
302
365
  separator
@@ -322,15 +385,33 @@ show_final_message() {
322
385
  }
323
386
 
324
387
  main() {
325
- if [[ $# -eq 2 && "$1" == "--ref" ]]; then
326
- if [[ ! "$2" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
327
- echo -e "\n ${P_FAIL}✖${P_NC} Invalid release ref: $2" >&2
328
- return 1
329
- fi
330
- BRANCH="$2"
331
- RELEASE_REF="$2"
332
- elif [[ $# -ne 0 ]]; then
333
- echo -e "\n ${P_FAIL}✖${P_NC} Usage: install.sh [--ref vX.Y.Z]" >&2
388
+ while [[ $# -gt 0 ]]; do
389
+ case "$1" in
390
+ --ref)
391
+ [[ $# -ge 2 && "$2" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] || {
392
+ echo -e "\n ${P_FAIL}✖${P_NC} Invalid or missing release ref" >&2
393
+ return 1
394
+ }
395
+ BRANCH="$2"
396
+ RELEASE_REF="$2"
397
+ shift 2
398
+ ;;
399
+ --commit)
400
+ [[ $# -ge 2 && "$2" =~ ^[0-9a-f]{40}$ ]] || {
401
+ echo -e "\n ${P_FAIL}✖${P_NC} Invalid or missing release commit" >&2
402
+ return 1
403
+ }
404
+ RELEASE_COMMIT="$2"
405
+ shift 2
406
+ ;;
407
+ *)
408
+ echo -e "\n ${P_FAIL}✖${P_NC} Usage: install.sh [--ref vX.Y.Z --commit SHA]" >&2
409
+ return 1
410
+ ;;
411
+ esac
412
+ done
413
+ if [[ -n "$RELEASE_REF" && ! "$RELEASE_COMMIT" =~ ^[0-9a-f]{40}$ ]]; then
414
+ echo -e "\n ${P_FAIL}✖${P_NC} Release installation requires an immutable commit SHA" >&2
334
415
  return 1
335
416
  fi
336
417
 
@@ -346,7 +427,7 @@ main() {
346
427
  setup_directories
347
428
  clone_repo
348
429
  create_symlink
349
- save_config
430
+ _finish_install
350
431
  show_final_message
351
432
  }
352
433
 
@@ -0,0 +1 @@
1
+ 0cb37fa9cbd9b82b371fdae9fd07c2041bfc4b19