Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
128 commits
Select commit Hold shift + click to select a range
e233f6a
Sessions that come back, own keys, pinch zoom, SSH server, web server…
claude Oct 24, 2026
0a52b5c
Native extractor: read a .tar.zst to its end, so that zstd checks its…
claude Oct 24, 2026
575bb78
Emulator test: export ssh_login, which expect runs in a new bash
claude Oct 24, 2026
35abde8
Emulator test: align cortex-emu-runner for 16 KB pages
claude Oct 24, 2026
49164be
Native extractor: decompress a .tar.zst on a thread of its own
claude Oct 24, 2026
ba1a75e
Hook: the app's files are root's in stat(), as the programs are root
claude Oct 24, 2026
6e98a24
Changelog: git's ownership check is fixed, not restored
claude Oct 24, 2026
45ecea3
Hook: accept() as accept4(), the call Android's seccomp filter is mad…
claude Oct 24, 2026
9a61e5b
Emulator test: time limits on adb, no packages on the 16 KB image
claude Oct 24, 2026
cc7e60a
Hook: sshd's privilege-separated user has root's ids
claude Oct 24, 2026
5f7e2fc
Hook: the fortified getgroups and recvfrom go through it too
claude Oct 24, 2026
2f15a34
Changelog: the emulator test's 16 KB run, ownership check and adb limits
claude Oct 24, 2026
1d4ffa7
Hook: sandbox limits that take away descriptors are not applied
claude Oct 24, 2026
c521330
Hook: closefrom() and close_range() leave the ARM translator's memory…
claude Oct 24, 2026
3993d55
Hook: know the ARM translator's memory file by its name
claude Oct 24, 2026
ce61c6e
Emulator test: record what a program sees of its executable and descr…
claude Oct 24, 2026
9054195
Static Go programs see the rootfs: Ubuntu's paths and programs for Go
claude Oct 25, 2026
f3cbede
Merge the Go support onto the branch's merged history (v1.26.38 was s…
claude Oct 25, 2026
fcb2f3b
A new look around the terminal: tabs, themed keys and drawer, seven m…
claude Oct 25, 2026
649be84
Scroll position and a way back to the end, a themed first-start dialo…
claude Oct 25, 2026
545da3c
Go programs patched in normal mode keep working in root mode; README
claude Oct 25, 2026
ce39a5b
Website and README: Go programs no longer need root mode; tabs and co…
claude Oct 25, 2026
ddd5c33
Tests: the tabs, keys, search bar, layouts, first-start dialog and sc…
claude Oct 25, 2026
721e363
Go programs: CORTEX_NO_GOSYS switch and an unpatched control in the e…
claude Oct 25, 2026
7812fe5
Settings theme: the interface's own containers and outline colors, no…
claude Oct 25, 2026
adc951a
Emulator test: a second session is started, and the scheme picker is …
claude Oct 25, 2026
ef79aa8
Scheme picker in the interface's colors; the emulator test retries op…
claude Oct 25, 2026
7b079fa
README and website: screenshots of the new interface, and the scheme …
claude Oct 25, 2026
56e138d
Package compatibility run: apt-get is not started by timeout in the b…
claude Oct 25, 2026
2316c10
Version 1.26.39: Go programs see Ubuntu, session tabs, seven more col…
claude Oct 25, 2026
cd303dd
Hook: Rust programs no longer crash in statx; reading /proc/self/fd o…
claude Oct 25, 2026
7d467e2
Merge the branch's merged history (PR #28 was squashed into main)
claude Oct 25, 2026
c39b5f5
Hook: shadow database and SELinux for Ubuntu's programs, the translat…
claude Oct 25, 2026
af04acc
Fix unclosed Kotlin comment in BaseUsers KDoc
claude Oct 25, 2026
4bc81d7
Version 1.26.40: faster program starts, sudo/su without root, Rust, r…
claude Oct 25, 2026
f98f6a1
Compat run: a MariaDB row; docs say MariaDB and redis run
claude Oct 25, 2026
958bd68
Emulator test: base users and groups, sudo
claude Oct 25, 2026
f1101fc
A color scheme chosen later reaches the cells of the sessions (#29)
claude Oct 25, 2026
29310aa
Review fixes: scheme remap by role, slots of vfork children given bac…
claude Oct 25, 2026
a56e0f4
Merge the branch's merged history (PR #31 was squashed into main)
claude Oct 26, 2026
5fedf73
No more ~/storage link to the shared storage (1.26.41)
claude Oct 26, 2026
bcb86ef
Fix the unclosed comment in the storage-link KDoc; test that no Kotli…
claude Oct 26, 2026
0803909
Test that a ~/storage link to another place is left alone
claude Oct 26, 2026
2474682
Start the next change from main
claude Oct 26, 2026
8cc804b
New app icon, held backspace keeps deleting, copy bar in the right pl…
claude Oct 26, 2026
5c9e1af
Chinese, Japanese, Korean and emoji take two cells instead of overlap…
claude Oct 27, 2026
0d6afc9
Start the next change from main
claude Oct 27, 2026
c5829ae
Fix a half left behind after inserting over a wide character; keep th…
claude Oct 27, 2026
f46bd24
Copy: a selection that ends on the first half of an emoji takes the w…
claude Oct 27, 2026
de3b864
Wide characters: mark the wrap padding, blank halves fully, cut copy;…
claude Oct 27, 2026
c9eb9df
Start the next change from main
claude Oct 27, 2026
4ae2b29
A cleaner drawer and tabs, and a white color scheme; 1.26.44
claude Oct 27, 2026
abc4169
Test the session tabs row by row, and their screen reader labels
claude Oct 27, 2026
515d3c7
Settings: the back arrow follows the color scheme
claude Oct 27, 2026
1d22b55
e2e: scroll the settings list all the way up before choosing the whit…
claude Oct 27, 2026
73f39f1
e2e: give the terminal the focus before typing in the second session
claude Oct 27, 2026
ed8ea5e
Hook: the real path of an open file keeps the rootfs in front of it
claude Oct 27, 2026
a72c5b1
Terminal: a character of the supplementary planes that wcwidth() give…
claude Oct 27, 2026
e6cfa61
Test: compare the colors with the ones the escape codes set
claude Oct 27, 2026
662a6ea
Review: fd links only for fd/N, the root tab says root, a plain curso…
claude Oct 27, 2026
07ce921
e2e: pictures for the README (neofetch, the end of the settings)
claude Oct 27, 2026
0d655de
Merge the earlier history of this branch (squash-merged as #37)
claude Oct 27, 2026
6157483
README and website: the session cards have no directory in their pict…
claude Oct 27, 2026
9b39eb7
e2e: neofetch for the README with the small logo and lines that fit
claude Oct 27, 2026
9d4fb47
tests/e2e: a script that makes the README's pictures from an emulator…
claude Oct 27, 2026
d085168
e2e: neofetch for the README without the emulator's model and archite…
claude Oct 27, 2026
dc33763
README and website pictures from the 1.26.44 emulator run
claude Oct 27, 2026
a342bd2
e2e: the neofetch picture only after a successful install and a finis…
claude Oct 27, 2026
bf700b5
The keyboard's Select all, Copy, Cut and Paste work in the terminal, …
claude Oct 28, 2026
94fc8d4
Merge the earlier history of this branch (squash-merged as #37 and #38)
claude Oct 28, 2026
c09f130
Review: Cut also does what delete does, the selection is the session'…
claude Oct 28, 2026
b3c825a
Review: the selected text is read under the emulator's lock
claude Oct 28, 2026
9855312
Go programs built for Android get Cortex's change, micro no longer ha…
ssfuisu Oct 30, 2026
4a0c199
Merge the earlier history of this branch (squash-merged as #37, #38, …
ssfuisu Oct 30, 2026
08c6397
Review: a clipboard note from the future is ignored, a timed-out clip…
ssfuisu Oct 30, 2026
372d6bc
The clipboard commands keep their deadline inside cortex-api (read -t…
ssfuisu Oct 30, 2026
49e591b
A directory that has lost its owner's rights is repaired when a name …
ssfuisu Oct 31, 2026
b0a4808
Merge the earlier history of this branch (squash-merged as #45)
ssfuisu Oct 31, 2026
2435128
The directory repair test also runs as a user that is not root but ha…
ssfuisu Oct 31, 2026
070181b
Review: the emulated hard link is looked up again after a directory w…
ssfuisu Oct 31, 2026
a108995
Hardware keyboard Ctrl, Alt and Shift reach the terminal; system() an…
ssfuisu Nov 2, 2026
54ad7bb
Review: system(NULL) checks for Ubuntu's shell, concurrent system() c…
ssfuisu Nov 2, 2026
536394a
Keep the branch on main after the 1.26.47 squash merge
claude Nov 2, 2026
b2e5618
Programs a shell starts take fewer page faults: the exec code is prep…
claude Nov 4, 2026
ee332e8
1.26.48: version and changelog
claude Nov 4, 2026
5789211
Review: the fork preparation does not wait for its lock (a signal han…
claude Nov 4, 2026
c134b52
Keep the branch on main after the 1.26.48 squash merge
claude Nov 4, 2026
71bd87b
Servers on a port the kernel picked are no longer connected to by the…
ssfuisu Nov 7, 2026
842ee05
Website: animated redesign
ssfuisu Nov 7, 2026
f4ffbfb
Review: a new socket starts without a chosen port, the qemu test find…
ssfuisu Nov 7, 2026
fdf9341
Pick the test port below the kernel's range, softer button shine in l…
ssfuisu Nov 7, 2026
76ced74
1.26.49
ssfuisu Nov 7, 2026
71394e0
Merge branch after squash
ssfuisu Nov 7, 2026
d549b0f
Website: drawn SVG icons instead of emoji
ssfuisu Nov 7, 2026
ac61da7
Merge branch after squash
ssfuisu Nov 7, 2026
76dade0
README: the phone's files are at /sdcard, near the top
ssfuisu Nov 7, 2026
9b3aa5d
Merge branch after README push
ssfuisu Nov 7, 2026
b9e3275
Programs find libraries through their RUNPATH (adb, fastboot); 1.26.50
ssfuisu Nov 7, 2026
f032f44
RUNPATH: search order as on Linux, any number of headers
ssfuisu Nov 7, 2026
7081ddd
Merge branch after squash
ssfuisu Nov 7, 2026
7bd9964
Desktop (X11) add-on, ld.so.conf.d/ldconfig/ldd, SysV shared memory; …
ssfuisu Nov 8, 2026
776bcb5
Review fixes: ld.so with its own --library-path, ldconfig marks ld.so…
ssfuisu Nov 8, 2026
5cfa504
Desktop and hook review fixes
ssfuisu Nov 8, 2026
e62136c
Merge branch state after #57 [skip ci]
ssfuisu Nov 8, 2026
efc45d6
Drawer: the desktop as a monitor icon next to the update check
ssfuisu Nov 8, 2026
a7e2789
Install: read the archive from the APK file with a CRC check, never m…
ssfuisu Nov 8, 2026
1883d92
installBootstrap: progress stays the last parameter (the trailing lam…
ssfuisu Nov 8, 2026
c6abc3f
Bootstrap reads: close the stream when the header read fails, copy fr…
ssfuisu Nov 8, 2026
c0cdec3
Merge branch state after #59 [skip ci]
ssfuisu Nov 8, 2026
b8d7e2d
SysV shm: a segment marked removed stays attachable for a while (GTK'…
ssfuisu Nov 9, 2026
4363f0b
shm: measure the IPC_RMID grace period with the boot clock
ssfuisu Nov 9, 2026
8d603dc
Merge branch state after 1.27.3
ssfuisu Nov 9, 2026
f7b7bbd
1.27.4: reload the custom terminal font when its file changes (#58)
ssfuisu Nov 9, 2026
42e651b
Merge branch state after 1.27.4
ssfuisu Nov 9, 2026
33acec7
Bug report: the desktop's state and its log
ssfuisu Nov 11, 2026
a4c9d50
1.27.5: XFCE runs (session bus, file type database, libpulse), PulseA…
ssfuisu Nov 11, 2026
4980a6e
Review: loader order and relative paths in dlopen, recvmmsg, desktop …
ssfuisu Nov 11, 2026
06734df
1.27.5.1: libraries' RUNPATH directories, locks on a memfd, fewer rea…
ssfuisu Nov 11, 2026
853aed6
Review: memfd locks by the memfd's inode, RTLD_DEEPBIND, 32-bit names…
ssfuisu Nov 11, 2026
1290602
32-bit: the headers already give these hooks their time64 names
ssfuisu Nov 11, 2026
83c894e
Merge branch state after 1.27.5.1
ssfuisu Nov 12, 2026
2c2e95c
1.27.6: selected text stays on the desktop, lstat keeps link sizes, r…
ssfuisu Nov 12, 2026
d056968
Review: /proc links are not kept, atomic cache fields, readlink of 0 …
ssfuisu Nov 12, 2026
b1f09da
Merge branch state after 1.27.6
ssfuisu Nov 12, 2026
b454569
Merge branch state after the README rewrite
ssfuisu Nov 12, 2026
dc8f670
Root mode: mount cgroups at /sys/fs/cgroup for container tools (#72)
ssfuisu Nov 13, 2026
0adbb48
1.27.7: honest wording, build attestation, compat page, compare bench…
ssfuisu Nov 13, 2026
248eb7a
Review: release tag at the built commit, publish only finished compat…
ssfuisu Nov 13, 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
28 changes: 27 additions & 1 deletion .github/workflows/build-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ jobs:
# Pinned: GitHub moves "ubuntu-latest" to Ubuntu 26.04 from 2026-10-19, and this job installs
# Ubuntu-specific cross-compilers. Move to a newer image deliberately, after testing it.
runs-on: ubuntu-24.04
# id-token and attestations: the build provenance of a release's APKs (see "Attest the APKs")
permissions:
contents: write
id-token: write
attestations: write

steps:
- name: Checkout Source Code
Expand Down Expand Up @@ -573,6 +578,23 @@ jobs:
if-no-files-found: error
retention-days: 3

# Anyone can check that a release's APK was built here from this repository's source, by this
# workflow, at this commit: gh attestation verify FILE --repo ssfuisu/cortex (signing/README.md)
- name: Checksums of the APKs
if: github.event_name != 'pull_request' && github.event.inputs.dry_run != 'true' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch')
working-directory: release-apks
run: |
sha256sum Cortex-arm64-v8a-signed.apk Cortex-armeabi-v7a-signed.apk > SHA256SUMS.txt
cat SHA256SUMS.txt
- name: Attest the APKs
if: github.event_name != 'pull_request' && github.event.inputs.dry_run != 'true' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch')
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
with:
subject-path: |
release-apks/Cortex-arm64-v8a-signed.apk
release-apks/Cortex-armeabi-v7a-signed.apk
- name: Create GitHub Release
if: github.event_name != 'pull_request' && github.event.inputs.dry_run != 'true' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch')
env:
Expand Down Expand Up @@ -626,13 +648,15 @@ jobs:
if grep -q '[^[:space:]]' changes.md; then
cat changes.md
else
echo "Cortex runs **Ubuntu 24.04 LTS (Noble Numbat)** with **glibc 2.39** natively on Android: no root, no PRoot, no containers, no virtual machine."
echo "Cortex runs Ubuntu 24.04 LTS (glibc 2.39) on Android through an \`LD_PRELOAD\` library: no root, no PRoot, no virtual machine."
fi
echo
echo "#### Downloads"
echo "- \`Cortex-arm64-v8a-signed.apk\`: 64-bit ARM (almost all current phones)"
echo "- \`Cortex-armeabi-v7a-signed.apk\`: 32-bit ARM"
echo
echo "\`SHA256SUMS.txt\` has the checksums. Each APK also has a build attestation: \`gh attestation verify Cortex-arm64-v8a-signed.apk --repo ${GITHUB_REPOSITORY}\` shows it was built by this repository's workflow from the tagged source."
echo
echo "How to verify a download: [signing/README.md](https://github.com/${GITHUB_REPOSITORY}/blob/main/signing/README.md). All versions: [CHANGELOG.md](https://github.com/${GITHUB_REPOSITORY}/blob/main/CHANGELOG.md)."
} > release-notes.md
echo "Release notes:"
Expand All @@ -641,6 +665,8 @@ jobs:
gh release create "$VERSION" \
Comment thread
ssfuisu marked this conversation as resolved.
Resolved
release-apks/Cortex-arm64-v8a-signed.apk \
release-apks/Cortex-armeabi-v7a-signed.apk \
release-apks/SHA256SUMS.txt \
--target "$(git rev-parse HEAD)" \
--title "Cortex Release $VERSION" \
--notes-file release-notes.md \
$PRERELEASE_FLAG
Expand Down
37 changes: 37 additions & 0 deletions .github/workflows/compat-nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ permissions:
contents: read
actions: read

# The nightly run of the latest release also updates docs/compat.json, which the website's
# compatibility page shows (see "Publish the results on the website").

concurrency:
group: package-compatibility
cancel-in-progress: false
Expand All @@ -35,6 +38,9 @@ jobs:
name: Packages on the emulator (API 34, arm64-v8a)
runs-on: ubuntu-24.04
timeout-minutes: 180
permissions:
contents: write
actions: read
steps:
- name: Checkout Source Code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down Expand Up @@ -67,6 +73,7 @@ jobs:
sudo udevadm trigger --name-match=kvm
- name: Install and try the packages on the emulator
id: emulator
uses: reactivecircus/android-emulator-runner@a421e43855164a8197daf9d8d40fe71c6996bb0d # v2.38.0
env:
E2E_COMPAT_ONLY: ${{ inputs.only }}
Expand All @@ -92,3 +99,33 @@ jobs:
name: package-compatibility
path: e2e-out
retention-days: 30

# Only the scheduled run of the latest release: a run with a chosen release, some packages
# or a dry run's APKs is not what the page describes. The commit only touches docs/ and
# skips CI.
- name: Publish the results on the website
# A run that timed out leaves part of the list: only a finished one replaces the page's
if: always() && steps.emulator.outcome == 'success' && github.event_name == 'schedule' && github.ref == 'refs/heads/main'
run: |
set -euo pipefail
RESULTS=e2e-out/cortex-e2e/results.txt
if [ ! -e e2e-out/cortex-e2e/done ] || [ ! -s "$RESULTS" ] || ! grep -q '^\(PASS\|FAIL\) ' "$RESULTS"; then
echo "No complete results to publish."
exit 0
fi
python3 tests/e2e/compat-json.py "$RESULTS" "$(cat tag.txt)" "$(date -u +%F)" > docs/compat.json
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add docs/compat.json
# Not a commit a day: only when a result or the release changed (the date alone does not)
if ! git diff --cached -U0 | grep '^[-+] ' | grep -vq '"date"'; then
echo "The results did not change."
exit 0
fi
git commit -m "Package compatibility of $(cat tag.txt) [skip ci]"
for i in 1 2 3; do
git pull --rebase --quiet origin main && git push origin HEAD:main && exit 0
sleep $((i * 10))
done
echo "Could not push the results." >&2
exit 1
34 changes: 34 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,40 @@ What changed in each version of Cortex Terminal, newest first. The release workf
of a GitHub release from the section of its version here. Older versions are described on the
[GitHub releases](https://github.com/ssfuisu/cortex/releases) page.

## 1.27.7 (2026-11-13)

### Fixed

- **Podman and other container tools found no cgroups in root mode** ([#72](https://github.com/ssfuisu/cortex/issues/72)).
The chroot got a fresh `/sys` with nothing mounted on `/sys/fs/cgroup`, and Android keeps its
own hierarchies somewhere else (`/dev/cpuset`, `/dev/cpuctl`, `/dev/memcg`, `/acct`), so
`podman info` listed no controllers and dropped the PIDs limit. Root mode now lays the phone's
cgroups out the way Ubuntu does: the cgroup2 tree when the phone has it at `/sys/fs/cgroup`
(Android 12 and later), otherwise one directory per v1 hierarchy, with Android's own hierarchies
bound in and the enabled controllers that have none, often `pids`, mounted. All of it stays in the
session's own mount namespace.

### Added

- **Proof of where an APK comes from.** Releases are now built on GitHub from the tagged source with
a build attestation for each APK, so `gh attestation verify Cortex-arm64-v8a-signed.apk --repo
ssfuisu/cortex` shows the workflow and commit it came from, and every release has
`SHA256SUMS.txt`. How to check both is in `signing/README.md`.
- **A benchmark you can run next to proot-distro.** `tests/bench/compare.sh` runs the same tests
(starting programs, file operations, `tar`, pure CPU, Python, `gcc`, `apt-cache`) in whatever it
runs in and prints a table, so Cortex and proot-distro can be compared on the same phone.
- **The nightly package test results on the website**, failures included, with what is known about
each failure: [ssfuisu.github.io/cortex/compat.html](https://ssfuisu.github.io/cortex/compat.html).
- **`SECURITY.md`**: what the app can and cannot do to a phone, every permission and why, and how to
report a problem. **`docs/containers.md`**: Podman and Docker in root mode, including the
iptables setting kernels without nftables NAT need.

### Changed

- The website, the README and the release notes say plainly how Cortex works: an `LD_PRELOAD`
library, the technique fakechroot and fakeroot use, rewritten for Android, with no `ptrace` and no
virtual machine. The old "natively, no containers" wording made it sound like more than that.

## 1.27.6 (2026-11-12)

### Fixed
Expand Down
35 changes: 33 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
<a href="https://github.com/ssfuisu/cortex/releases/latest"><b>Download</b></a> ·
<a href="#how-it-actually-works">How it works</a> ·
<a href="#what-is-real-and-what-is-emulated">Real vs emulated</a> ·
<a href="https://ssfuisu.github.io/cortex/compat.html">Package tests</a> ·
<a href="SECURITY.md">Security</a> ·
<a href="CHANGELOG.md">Changelog</a> ·
<a href="https://github.com/ssfuisu/cortex/issues">Issues</a> ·
<a href="https://t.me/ratzgn">Telegram</a>
Expand Down Expand Up @@ -97,7 +99,7 @@ The library is about 10,000 lines of C in [`app/src/main/cpp/hook`](app/src/main
## Install

1. Download from [Releases](https://github.com/ssfuisu/cortex/releases/latest): `Cortex-arm64-v8a-signed.apk` for nearly every phone, `Cortex-armeabi-v7a-signed.apk` for 32-bit ARM.
2. Optional: check the signature as described in [`signing/README.md`](signing/README.md).
2. Optional: check where it comes from. From 1.27.7 on every APK is built on GitHub from the tagged source, `gh attestation verify Cortex-arm64-v8a-signed.apk --repo ssfuisu/cortex` proves it, and `SHA256SUMS.txt` is next to it. The signing key is in [`signing/README.md`](signing/README.md).
3. Install and open it. Ubuntu is unpacked on the first start, then you get a shell.

Android 7.0 or newer. Like Termux, the app targets API level 28, the last one that lets an app run programs from its own data directory.
Expand Down Expand Up @@ -169,14 +171,43 @@ Programs run at native speed. The library only costs something on calls that tak

Run [`tests/bench/run-micro.sh`](tests/bench/run-micro.sh) inside Cortex to measure your own phone.

**Comparing with proot-distro yourself.** [`tests/bench/compare.sh`](tests/bench/compare.sh) runs the same tests anywhere (starting programs, creating, listing and reading files, `tar`, `find`, pure CPU, Python start-up, a compile, `apt-cache`) and prints a table. Run it in Cortex and in proot-distro on the same phone:

```bash
curl -fsSL https://raw.githubusercontent.com/ssfuisu/cortex/main/tests/bench/compare.sh | bash
```

It also says what it detected it runs in, so the two tables can't get mixed up.

## Root mode

On a rooted phone (Magisk, KernelSU, APatch), *Settings > Root mode* runs new sessions as real root in a chroot of the same Ubuntu files. The library is not loaded there. Mounts, real owners, hard links, set-user-ID programs and low ports work as on a PC. Each session gets its own mount namespace, so nothing is mounted for Android or other apps. Turning it off gives the files back to the app.

Containers need root mode. Since 1.27.7 it also mounts the phone's cgroups at `/sys/fs/cgroup`, and [docs/containers.md](docs/containers.md) has the steps for Podman and Docker, including the iptables setting many Android kernels need.

## Is it safe for the phone?

In normal mode Cortex is an ordinary app: everything you run in it runs as the app's own Android user, in the app sandbox. It can't change Android, the system or other apps' private data, it can't brick anything, and uninstalling removes everything in the app's own storage. Programs only *think* they are root. The one thing to keep in mind: once you allow storage access, commands can change and delete your files in `/sdcard`, and uninstalling doesn't undo that. [SECURITY.md](SECURITY.md) explains every permission the app asks for and what the app itself connects to (Ubuntu's mirrors and GitHub's release API, nothing else).

## FAQ

**What do people actually use it for?**
Programming on the phone or tablet (Python, Node.js, C, Rust, Go, git), small servers and bots (nginx, Redis, MariaDB, a Telegram bot), SSH into Cortex from a computer with `cortex-sshd`, scripts that use the phone through `cortex-api`, and the occasional desktop program on the built-in X11 desktop.

**Will every Ubuntu package work?**
Most command-line software does, and a nightly job installs and runs a set of common packages on an emulator. What needs the things in the list above (systemd services, other users, raw devices) does not work in normal mode.
Most command-line software does. Every night a job installs a set of common packages on an emulator and tests each one, the [latest results](https://ssfuisu.github.io/cortex/compat.html) are public, failures included. What needs the things in the list above (systemd services, other users, raw devices) does not work in normal mode.

**Can I run Docker or Podman?**
Only in root mode, see [docs/containers.md](docs/containers.md). Containers need namespaces, mounts and cgroups, and Android never gives those to an app.

**Is there a GUI?**
Yes, as an add-on: *Desktop* in the menu installs Openbox or XFCE (and Firefox if you want) and shows it inside the app. It's drawn by the CPU, no GPU, so it suits editors and browsing more than games.

**Does it work on 32-bit phones?**
Yes, `Cortex-armeabi-v7a-signed.apk` is Ubuntu's armhf. Every change builds it and installs and sets it up on an emulator, but the emulator can't run 32-bit Ubuntu programs, so the in-terminal tests only run for 64-bit. Reports from 32-bit phones help.

**Why does Go need special code?**
Go programs don't use the C library, they make their system calls themselves, so a preloaded library never sees them. Before a Go 1.19+ program starts for the first time, Cortex adds a small piece of code to it that does the same path mapping.

**Do single-file downloads work?**
Static C and C++ programs do, and Go programs built with Go 1.19 or newer. Older or 32-bit Go programs run but see Android's files instead of Ubuntu's.
Expand Down
33 changes: 33 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Security

## Reporting a problem

If you find a security problem in Cortex, please don't open a public issue. Write on [Telegram](https://t.me/ratzgn), or open an issue that only says you found something and we'll take it private from there. I'll answer within a few days and credit you in the changelog if you want.

## What Cortex can and cannot do to your phone

**In normal mode Cortex is an ordinary Android app.** Everything it runs, `apt`, `gcc`, a server you start, runs as the app's own Android user, inside the app sandbox, with the same rights the terminal app itself has. Programs think they are root because the library answers "uid 0", but the kernel still sees the app's user. That means:

- It cannot change Android, the system partition, the bootloader or other apps' private data. It cannot brick the phone. A broken package can break Cortex's own Ubuntu, and clearing the app's data (or reinstalling it) gives you a fresh one.
- Uninstalling the app removes everything in its own storage. Nothing is left in the system.
- **Shared storage is the exception.** If you allow storage access, programs in Cortex can read, change and delete your files in `/sdcard` (photos, downloads, documents) just like a file manager can. A bad `rm` there is not undone by clearing data or uninstalling, and files programs create there stay after uninstalling.
- Ubuntu's files live in the app's private storage (`/data/data/org.cortex.terminal`), which other apps cannot read. Your phone's shared storage (`/sdcard`) is only reachable after you allow it.
- Packages come from Ubuntu's own mirrors and `apt` checks Ubuntu's signatures, the same as on a PC. Cortex ships Ubuntu's keyring and does not add any other package source.

**Root mode is different.** It only exists on phones you rooted yourself, only runs after you turn it on and your root manager (Magisk, KernelSU, APatch) asks you, and then sessions really are root. A root session can do anything root can do on Android. Each session gets its own mount namespace so its mounts don't leak into Android, but a root shell is a root shell: use it like you would `su`.

## Permissions

| Permission | Why |
|---|---|
| `INTERNET`, `ACCESS_NETWORK_STATE` | `apt`, `git`, `curl` and every other network program. The app itself only talks to Ubuntu's mirrors (package lists and the fastest-mirror check) and to `api.github.com` (is there a new Cortex release). No analytics, no ads, no tracking. |
| `FOREGROUND_SERVICE`, `FOREGROUND_SERVICE_SPECIAL_USE`, `WAKE_LOCK` | Keeps your sessions running when the app is in the background, with the notification you see. The wake lock is only taken when you turn it on in that notification. |
| `POST_NOTIFICATIONS`, `VIBRATE` | The session notification, `notify-send` and `cortex-api` notifications, and the bell. |
| `READ_EXTERNAL_STORAGE`, `WRITE_EXTERNAL_STORAGE`, `MANAGE_EXTERNAL_STORAGE` | `/sdcard` in the terminal. The app asks once at the start; say *Later* and everything else works, only `/sdcard` stays closed. |
| `REQUEST_INSTALL_PACKAGES` | The in-app updater installs a newer Cortex APK, after you tap it. |

Cortex has no accessibility service, reads no contacts, SMS, location, camera or microphone, and the Files app entry only shows the Cortex home folder to the system file picker.

## Checking a download

From 1.27.7 on, every release is built on GitHub's servers from the tagged source and comes with a build attestation and `SHA256SUMS.txt`. How to check both, and the signing key fingerprints, is in [signing/README.md](signing/README.md). The code is all here: the library that does the path mapping is [`app/src/main/cpp/hook`](app/src/main/cpp/hook), the root mode helper is [`app/src/main/cpp/root/cortex-chroot.c`](app/src/main/cpp/root/cortex-chroot.c).
4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ android {
// from the app's writable data directory (filesDir). Android 10+ (targetSdk >= 29)
// enforces SELinux W^X restrictions preventing execution from writable app data storage.
targetSdk = 28
versionCode = 12706
versionName = "1.27.6"
versionCode = 12707
versionName = "1.27.7"
// UbuntuOnDeviceTest (app/src/androidTest), on Firebase Test Lab
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

Expand Down
Loading
Loading