# Cthulhu Tentacles A repository of experimental Tentacles for Cthulhu-Go, maintained by Storm Dragon. It contains [Hello World](hello-world/README.md) and the development [Wormhole tentacle](wormhole/README.md). Repository material and both Tentacles are licensed under GPL-3.0-or-later; see [LICENSE](LICENSE) and [CONTRIBUTING.md](CONTRIBUTING.md). `repository.toml` identifies this source. `catalog.toml` explicitly lists its Tentacles, each in a direct top-level folder with its own `tentacle.toml`. The catalog lists native packages for Linux amd64 and arm64. Hello World 0.1.0 is available from the [release page](https://git.stormux.org/storm/cthulhu-tentacles/releases/tag/hello-world-v0.1.0). Wormhole 0.1.1 requires the matching reader with interactive Tentacle support. Its release archives are prepared for upload to the Wormhole release page; installation becomes available once both archives have been uploaded. Local build artifacts live in the ignored `dist/` directory. The discovery source URL is `https://git.stormux.org/storm/cthulhu-tentacles`. Tentacles are independent of the Git hosting service. Discovery reads TOML files from an HTTPS Git repository; installation downloads the HTTPS URLs explicitly listed in its catalog. Packages can live in pretty much any git provider, on an HTTPS web server, or in object storage with stable public HTTPS links. The reader does not call a hosting service's release API. Keeping packages in separate downloads avoids putting binaries into Git history. To write your own, follow the [creation and release tutorial](#create-and-release-your-own-tentacle). ## Build and check Keep the matching experimental `cthulhu-go` checkout beside this directory. The `go.mod` local replacement uses its Go SDK, including the new uninterruptible presentation option. Building requires Go 1.24 or newer; checks also require Python 3.11 or newer and Make. ```sh make build make check ``` The executables are `hello-world/bin/hello-world` and `wormhole/bin/wormhole-tentacle`; both are ignored by Git. Checks validate the TOML metadata, registration and suggested bindings, and invoke both Tentacles through the actual Cthulhu-Go host. They produce no speech or Braille and leave the running reader alone. ## Repository installation With the updated reader, open global Cthulhu Preferences → Tentacles → Add repository and enter the HTTPS Git URL above. Refresh an already added source. Select Hello World or Wormhole and press Space to review its source/version and requested access, then **Install and enable**. Wormhole requests internal forms and focused-item/clipboard access as well as speech/Braille. **Install** instead downloads and saves the package disabled. The reader verifies the catalog's SHA-256 digest, package identity and native architecture before saving a launch entry. Catalogs without a published compatible release show a clear message and remain unchecked. No source builds or install scripts run during this workflow. Resource limits do not make untrusted same-user programs safe. After installation, refresh status until Active and use Cthulhu+Alt+Shift+H. Disable retains the package; re-enabling and reader startup use installed files without network access. Removing the source retains installed Tentacles. Removing a managed Tentacle stops it and deletes its package files after shutdown. Package updates are not implemented in this slice. See the [Hello World live checks](hello-world/README.md#live-verification). ## Prepare and publish packages ```sh make check make package ``` `tools/package.py` cross-builds statically linked native binaries for Linux amd64 and arm64 from the matching adjacent SDK. It creates deterministic archives containing `tentacle.toml`, `bin/hello-world`, `README.md` and `LICENSE`, plus `dist/SHA256SUMS` and a generated `dist/catalog.toml`. Go builds use readonly module metadata, trim paths and omit VCS stamping. To build one architecture: ```sh python3 -B tools/package.py --architecture amd64 ``` Validate the actual host-architecture package through Cthulhu-Go's installer and command host, without changing the running reader: ```sh cd ../cthulhu-go CTHULHU_TENTACLE_RELEASE_ROOT=../cthulhu-tentacles GOCACHE=/tmp/cthulhu-go-cache go test ./internal/tentacle -run '^TestBuiltHelloWorldPackageThroughHost$' -count=1 -v ``` Before publication, review both repositories' changes and contributing guides. Commit the reviewed source with the matching SDK available; select that source revision for the `hello-world-v0.1.0` release. In the Git hosting release page, upload `dist/hello-world-0.1.0-linux-amd64.tar.gz`, `dist/hello-world-0.1.0-linux-arm64.tar.gz` and `dist/SHA256SUMS`. Publish and verify the download URLs and digests **before** publishing the matching catalog change. Copy the reviewed `dist/catalog.toml` to `catalog.toml`, review the diff, and submit/push it explicitly. A release filename/version must never be reused for changed content; bump the version before preparing a later release. The package command creates local artifacts only. It does not create commits, releases, tags, uploads or pushes. Prepared catalog changes are not evidence that the corresponding release is already available. Live shortcut, speech, Braille, lock/handoff and interruption checks remain necessary after installation. An explicit restart is needed when changing the reader itself. For source-only local testing, the development launch procedure remains in [Hello World](hello-world/README.md#local-launch). ## Create and release your own Tentacle This tutorial creates a native Go Tentacle named **Greeting**, then makes it installable from a repository. The SDK and package format are experimental; use a matching Cthulhu-Go checkout. Currently the installer supports native Linux executables, protocol 1.0, command registration and optional presentation access. Python SDK development is separate from this native package workflow. Broader APIs and automatic package updates are still pending. ### 1. Set up the source repository You need Go 1.24 or newer, Git, Python 3.11 or newer for this repository's checks, GNU tar and gzip. Keep your new repository beside `cthulhu-go`. From their parent directory: ```sh mkdir -p greeting-tentacles/greeting/src cd greeting-tentacles git init -b main ``` Create these files: ```text greeting-tentacles/ repository.toml catalog.toml go.mod .gitignore greeting/ tentacle.toml README.md LICENSE src/main.go ``` Use this `go.mod`: ```go module example.org/greeting-tentacles go 1.24.0 require git.stormux.org/storm/cthulhu-go v0.0.0 replace git.stormux.org/storm/cthulhu-go => ../cthulhu-go ``` The SDK import path names its source project; it does not constrain where you host your own repository or downloads. Record the tested reader/SDK revision in release notes, since this local replacement does not pin it. Add `/dist/` and `/greeting/bin/` to `.gitignore`. Give `greeting/README.md` a description, shortcut, requirements and test procedure. Put the full license text in `greeting/LICENSE`; choose terms you have permission to distribute. For an example derived from Hello World, preserve its notices and GPL-3.0-or-later license. Include source corresponding to your executable. Create `repository.toml`: ```toml format-version = 1 id = "example.greeting-tentacles" name = "Greeting Tentacles" description = "A small example Tentacle repository." ``` Create the initial `catalog.toml`: ```toml format-version = 1 [[tentacles]] id = "greeting" manifest = "greeting/tentacle.toml" ``` Manifests belong in direct top-level folders. Repository and Tentacle IDs start with a lowercase letter and contain only lowercase letters, digits, dots and hyphens, up to 128 characters. Keep IDs stable: the reader uses the source URL, repository ID and Tentacle ID to identify an installation. A catalog can list several Tentacles, each with its own manifest and releases. Entries without releases can be discovered, but cannot yet be installed. ### 2. Describe the package and permissions Create `greeting/tentacle.toml`, replacing the example source URL with your own HTTPS Git URL before publication: ```toml format-version = 1 id = "greeting" name = "Greeting" description = "Speak and braille a short greeting." version = "0.1.0" authors = ["Contributor"] license = "GPL-3.0-or-later" source = "https://example.org/projects/greeting-tentacles.git" help = "README.md" runtime = "native" platforms = ["linux"] entry-point = ["bin/greeting"] apis = ["commands", "presentation"] capabilities = ["presentation"] [protocol] major = 1 minor = 0 ``` Paths refer to the archive's root, not the source repository's root. The entry point is an executable followed by optional literal arguments, not a shell command. Presentation access allows speech and Braille results through the reader. A command-only package uses `apis = ["commands"]` and `capabilities = []`, without requesting presentation in command registration. Other package API or capability combinations are currently unsupported. ### 3. Implement a command Create `greeting/src/main.go`: ```go package main import ( "context" "encoding/json" "os" "git.stormux.org/storm/cthulhu-go/sdk/go/tentacle" ) func say_greeting(ctx context.Context, _ json.RawMessage) (any, error) { if err := ctx.Err(); err != nil { return nil, err } return tentacle.Presentation{ Speech: "Hello from Greeting!", Braille: "Hello from Greeting!", }, nil } func main() { command := tentacle.Command{ ID: "greet", Title: "Greeting", Description: "Speak and braille a greeting", Presentation: true, Bindings: []tentacle.Binding{ {Layout: "desktop", Scope: "global", Key: "Cthulhu+Ctrl+Alt+F10"}, {Layout: "laptop", Scope: "global", Key: "Cthulhu+Ctrl+Alt+F10"}, }, Handle: say_greeting, } if tentacle.Serve(os.Stdin, os.Stdout, []tentacle.Command{command}) != nil { os.Exit(1) } } ``` The SDK handles the JSON-RPC session and registration. Stdout belongs exclusively to that protocol; send limited diagnostics to stderr. Respect the supplied context when doing work, including network requests. The reader owns input, speech, Braille, user settings and lifecycle safeguards. Use its public SDK instead of reader internals or output backends. Core and user bindings take precedence over suggested shortcuts; users can remap Tentacle commands. Greeting uses ordinary interruptible presentation. Hello World demonstrates `Uninterruptible: true`; use that deliberately, rather than for every message. See its [output behavior](hello-world/README.md) for cancellation, the 30-second protection bound and speech backend limitations. ### 4. Build and test before packaging From `greeting-tentacles`: ```sh gofmt -w greeting/src/main.go mkdir -p greeting/bin CGO_ENABLED=0 GOCACHE=/tmp/cthulhu-go-cache go build -buildvcs=false -mod=readonly -trimpath -o greeting/bin/greeting ./greeting/src GOCACHE=/tmp/cthulhu-go-cache go test ./... ``` Invoke the compiled command through the development host. From `cthulhu-go`: ```sh GOCACHE=/tmp/cthulhu-go-cache go run ./cmd/tentacle-host -presentation -command greet "$PWD/../greeting-tentacles/greeting/bin/greeting" ``` Expect JSON containing `speech` and `braille`, both `Hello from Greeting!`. This runs the executable through the actual command host, but produces no speech or Braille. The harness's default mode has no resource containment; use it only with development code you trust. Protocol tests do not establish live behavior. For a live test, follow [Hello World's development launch procedure](hello-world/README.md#local-launch), substituting your absolute executable path and launch ID `greeting`, with `Presentation: true` in the launch JSON. `--replace --tentacles` replaces the current Go reader and selects the development list instead of the saved registry; run it explicitly when ready. Rebuild the reader first if its implementation changed. Production activation requires delegated cgroup resources and does not silently fall back when delegation fails. Test the shortcut, learn mode, remapping, ordinary reading and web navigation on Xorg. Verify speech modes, interruption, screen lock, terminal handoff, disable and connected Braille where affected. See [CONTRIBUTING.md](CONTRIBUTING.md) for review and regression requirements; contributors remain responsible for reviewing and testing AI-assisted changes. ### 5. Create architecture-specific archives The `make build`, `make check` and `tools/package.py` commands in **this** repository build/check Hello World and Wormhole. `tools/package.py --tentacle wormhole` packages Wormhole; the default still packages Hello World. The checker pins the current versions and expected Tentacles. Adapt those tools and checks when extending this repository, or use this standalone procedure for Greeting. From `greeting-tentacles`, stage the root manifest, documentation and license: ```sh mkdir -p dist/greeting-package/bin cp greeting/tentacle.toml greeting/README.md greeting/LICENSE dist/greeting-package/ ``` Run the following in Bash for version `0.1.0`. Change the version together with the manifest on later releases. `set -euo pipefail` stops on build/archive failures. ```bash set -euo pipefail version=0.1.0 for architecture in amd64 arm64; do CGO_ENABLED=0 GOOS=linux GOARCH="$architecture" GOCACHE=/tmp/cthulhu-go-cache \ go build -buildvcs=false -mod=readonly -trimpath -ldflags='-s -w' \ -o dist/greeting-package/bin/greeting ./greeting/src tar --sort=name --mtime=@0 --owner=0 --group=0 --numeric-owner \ -C dist/greeting-package -cf - tentacle.toml README.md LICENSE bin \ | gzip -n > "dist/greeting-$version-linux-$architecture.tar.gz" done sha256sum "dist/greeting-$version-linux-amd64.tar.gz" \ "dist/greeting-$version-linux-arm64.tar.gz" \ | sed 's@ dist/@ @' > dist/SHA256SUMS ``` Each archive must have this layout, without an enclosing `greeting/` folder: ```text tentacle.toml README.md LICENSE bin/greeting ``` The executable must be a regular, executable Linux ELF file for the advertised architecture. Static Go builds avoid undeclared shared-library dependencies. The reader rejects unsafe paths, symbolic/hard links and special files. Current limits are 32 MiB per compressed download, 64 MiB expanded file data and 1,024 archive entries. Installation runs no install scripts or source builds. Package ID, name, version and capabilities must match the repository manifest. Inspect each archive with `tar -tzf`; from `dist`, run `sha256sum -c SHA256SUMS`. Run each extracted executable through the host on its matching architecture; a cross-build alone is not an execution test. The staging executable above ends with the last architecture built, so use the appropriate extracted archive for your local test. Keep the exact archives for publication. Normalized tar/gzip metadata supports repeatable packaging; compare hashes when rebuilding with the same source and toolchain. ### 6. Publish downloads, then advertise them Review the source diff and tests before committing. Push reviewed source to your chosen Git host. For later versions, use a release branch while preparing assets so the default branch's existing manifest and catalog remain consistent. Create a release/tag such as `greeting-v0.1.0` targeting that source commit. If using a separate download server, record that source revision alongside the files. Upload both `.tar.gz` files and `SHA256SUMS`, and make the release public when the assets are ready. Keep `dist/` and executables out of Git. Use stable direct HTTPS download URLs. Release attachments are convenient but optional; an ordinary HTTPS file server works too. URLs cannot embed credentials or fragments, and redirects must remain HTTPS. The reader has no authenticated private-download workflow; expiring signed URLs are unsuitable for a durable catalog. Download the published files again and check `sha256sum -c SHA256SUMS` against those downloads. Check the archive manifest and executable. Only then append these tables after Greeting's entry in `catalog.toml`: ```toml [[tentacles.releases]] platform = "linux" architecture = "amd64" url = "https://downloads.example.org/greeting/0.1.0/greeting-0.1.0-linux-amd64.tar.gz" sha256 = "REPLACE_WITH_THE_AMD64_ARCHIVE_SHA256" [[tentacles.releases]] platform = "linux" architecture = "arm64" url = "https://downloads.example.org/greeting/0.1.0/greeting-0.1.0-linux-arm64.tar.gz" sha256 = "REPLACE_WITH_THE_ARM64_ARCHIVE_SHA256" ``` Replace the URLs and placeholders with published URLs and their actual 64-character lowercase hexadecimal hashes. These tables belong to the preceding `[[tentacles]]`; each platform/architecture pair may appear once. Keep the manifest version equal to the version inside the packages. Include only architectures you actually publish. For several Tentacles, preserve the existing entries: this repository's generated `dist/catalog.toml` preserves catalog entries and prepares release metadata for the selected Tentacles. Review it before publication. Review and push the matching catalog and manifest together to the repository's default branch, which discovery fetches. Never replace a release file with changed content: increase the version, create new archives and URLs, then publish a new catalog revision. Refresh updates available metadata, not installed packages. To test a newer version with the current reader, remove that managed Tentacle and install it again after refreshing the source. ### 7. Verify the published installation Open global Preferences → Tentacles → Add repository in the matching reader and enter your HTTPS Git URL, or refresh an existing source. Select Greeting and press Space. Check source, version and presentation access in the first-use dialog, then choose **Install and enable**. Confirm status becomes Active and test Cthulhu+Ctrl+Alt+F10 and the live checks from step 4. Also check **Install** without enabling, enable/disable, offline reader restart and removal. Disabled packages retain their files; installed packages launch offline. Removing a source retains installed packages; removing a managed Tentacle stops it and deletes its package files. Keep ordinary reader commands, keyboard navigation and speech/Braille preferences working. For no compatible release, check architecture and refresh the source. For download/checksum errors, verify the exact public URL and digest. For package metadata errors, compare the archive manifest with the repository manifest. For activation failures, check the reader log for protocol, executable or cgroup errors. Resource limits constrain consumption; they do not sandbox access to account files or network. Install publishers you trust and state any remaining unverified live behavior in release notes.