Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 9 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,18 @@
# IKSIR_LOG_DIR=~/.local/state/iksir

# =============================================================================
# Agent Runtime (OpenCode)
# The Nest (hum)
# =============================================================================

# OpenCode server URL (default: http://localhost:4096)
# IKSIR_OPENCODE_SERVER=http://localhost:4096
# Iksir runs no models. It nestles at a humd and prompts whichever hive you
# have kindled there. Both settings below are optional.

# MCP server port (default: 3100)
# IKSIR_MCP_PORT=3100
# Explicit thrum socket. Discovered if unset:
# HUM_THRUM_SOCK -> HUM_SOCKET -> humd's runtime.json -> $XDG_STATE_HOME/hum/thrum.sock
# HUM_THRUM_SOCK=/run/user/1000/hum/thrum.sock

# Model to name on each prompt. Unset = the nest decides.
# IKSIR_HUM_MODEL=claude-sonnet-4-6

# =============================================================================
# Issue Tracker (Linear)
Expand Down
3 changes: 3 additions & 0 deletions Orchfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SERVICE iksir
RUN ${HOME}/.deno/bin/deno run --allow-all --env=${HOME}/.local/share/iksir/src/.env ${HOME}/.local/share/iksir/src/src/main.ts
RESTART always
54 changes: 41 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,27 @@ The modern world has forgotten the scent of real alchemy - the sharp bite of sul
curl -fsSL https://raw.githubusercontent.com/adiled/iksir/main/install | bash
```

Prerequisites: [Deno](https://deno.com), [OpenCode](https://opencode.ai)
Prerequisites: [Deno](https://deno.com), and a [hum](https://github.com/adiled/hum) nest.

The kindling ritual clones the source, creates XDG directories, copies sacred templates, installs the `iksir` CLI to `~/.local/bin/`, registers daemon services, and consecrates agent incantations. Edit `~/.local/share/iksir/src/.env` to bind your keys, then `iksir start`.
**Iksīr kindles no furnace of its own.** It nestles at a `humd` as a forager bee and prompts whatever hive you have chosen. What burns there is yours to decide — that choice was never Iksīr's to make. It asks only two things of al-Kimyawi:

- a **worker** bee nestled in the same nest, so there is something to prompt
- something in that nest providing **`fs`** — [`humfs`](https://github.com/adiled/hum/tree/main/hives/humfs), or a worker that carries its own tools

A murshid without `fs` can contemplate the runūz but never inscribe them.

```bash
hum hive humfs install # the filesystem surface
hum hive <your-worker> install # whatever you would have think for you
```

The kindling ritual clones the source, creates XDG directories, copies sacred templates, installs the `iksir` CLI to `~/.local/bin/`, registers daemon services, and consecrates the ruqan. Edit `~/.local/share/iksir/src/.env` to bind your keys, then `iksir start`.

Iksīr can also be supervised as a bee in its own right — an `Orchfile` sits at the root, so `hum hive ~/iksir install` hands it to orchd.

```bash
iksir divine # divine the state of the Great Work
iksir transmute # pull latest essence, sync vessels, rekindle
iksir rekindle mcp # rekindle just the MCP crucible
iksir verify # verify the formulae and runes
```

Expand Down Expand Up @@ -61,22 +74,37 @@ Iksīr transmutes raw **wasfa** (وصفة - formulae) into perfected **dhahab**
Kimyawi (كيميائي - You, the Human Alchemist)
|
Iksīr (إكسير - The Elixir)
| (Served by the Khuddām - خدّام - Sacred Servants)
|- Munadi منادي - calls forth the workers
|- Katib كاتب - inscribes all transformations
|- Arraf عرّاف - divines intent from utterances
|- Saail سائل - divines truth from questions
|- Mumayyiz مميّز - separates dhahab from khabath
|- Raqib رقيب - guards against fasad (corruption)
|- Hayat حياة - keeps vigil, performs the night rites
'- Munaffidh منفذ - executes the transmutation
|
|- Khuddām (خدّام - Sacred Servants)
| Eternal, bound to the workshop. One of each. They are Iksīr.
|
| |- Munadi منادي - calls forth the workers
| |- Katib كاتب - inscribes all transformations
| |- Arraf عرّاف - divines intent from utterances
| |- Saail سائل - divines truth from questions
| |- Mumayyiz مميّز - separates dhahab from khabath
| |- Raqib رقيب - guards against fasad (corruption)
| |- Hayat حياة - keeps vigil, performs the night rites
| '- Munaffidh منفذ - executes the transmutation
|
'- Murshidun (مرشدون - The Guides)
Summoned per wasfa, many, but only one at the flame. They perish
when the work is done. Iksīr does not contain them; it tends them.
Each dwells in its own inā', on its own branch, and never touches
the source directly.
|
'- Sani (صانع - The Craftsman)
Summoned by a Murshid for one waṣfa. Inscribes the runūz.
Returns to the void when the work is complete.
|
Buwtaqa (بوتقة - The Crucible)
|
Athanor (أثانور - The Sacred Furnace)
```

The sacred tools are forged within: transmutation rites, formula inscription, essence decanting. Additional instruments may be consecrated through the mystical registry.
The Khuddām are Iksīr's own flesh. The Murshidun are not — they are cells burning in a nest al-Kimyawi has kindled, reached across the thrum. Iksīr summons them, guards the single flame among them, and carries their words to you; what they are made of was never Iksīr's to decide.

The **Ālāt al-Iksīr** (آلات الإكسير - instruments of the Elixir) are forged within: transmutation rites, formula inscription, essence decanting. They are advertised to the nest in Iksīr's own hello, and a summons returns to them by name. Additional instruments may be consecrated through the sijill.

## The Sacred Laws

Expand Down
2 changes: 1 addition & 1 deletion db/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ export function aghlaaqQaidatBayanat(): void {

/**
* Insert an IPC hadath into the ahdath table.
* Used by MCP servers to forward tool calls to the daemon.
* Used by the instruments to forward their nida to the daemon.
*/
export function adkhalaHadath(
naw: "pm",
Expand Down
3 changes: 1 addition & 2 deletions deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,14 @@
"check": "deno run --allow-all --env=.env src/cli.ts check",
"sync": "deno run --allow-all --env=.env src/cli.ts sync",
"test": "deno test --allow-all",
"typecheck": "deno check src/main.ts src/mcp/iksir-mcp.ts src/mcp/serve.ts src/cli.ts",
"typecheck": "deno check src/main.ts src/alat/alat-al-iksir.ts src/cli.ts",
"fmt": "deno fmt",
"lint": "deno lint"
},
"imports": {
"@std/": "jsr:@std/",
"@std/assert": "jsr:@std/assert@^1",
"@std/testing": "jsr:@std/testing@^1",
"@opencode/sdk": "npm:@opencode-ai/sdk@^1.2.10",
"@db/sqlite": "jsr:@db/sqlite@^0.12.0"
},
"compilerOptions": {
Expand Down
4 changes: 1 addition & 3 deletions iksir.json.example
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,5 @@
"repo": "your-repo",
"operatorUsername": "your-github-username"
},
"opencode": {
"server": "http://localhost:4096"
}
"hum": {}
}
14 changes: 8 additions & 6 deletions iksir.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -177,15 +177,17 @@
},
"additionalProperties": false
},
"opencode": {
"hum": {
"type": "object",
"description": "Agent runtime connection.",
"description": "The nest Iksir nestles at. Both fields are optional: the socket is discovered, and the model is chosen by kindling a hive.",
"properties": {
"server": {
"miqbas": {
"type": "string",
"description": "OpenCode server URL.",
"format": "uri",
"default": "http://localhost:4096"
"description": "Explicit thrum socket path. Omit to discover via HUM_THRUM_SOCK, HUM_SOCKET, humd's runtime.json, then $XDG_STATE_HOME/hum/thrum.sock."
},
"namudhaj": {
"type": "string",
"description": "Model id to name on each prompt. Omit to let the nest decide."
}
},
"additionalProperties": false
Expand Down
96 changes: 25 additions & 71 deletions install
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ BIN_DIR="$HOME/.local/bin"
IKSIR_REPO="https://github.com/adiled/iksir.git"
IKSIR_SRC="$IKSIR_DATA/src"

AGENT_PORT="${IKSIR_AGENT_PORT:-4096}"
MCP_PORT="${IKSIR_MCP_PORT:-3100}"

OS="$(uname -s)"
CMD="${1:-install}"
Expand All @@ -34,8 +32,14 @@ check_prereqs() {
DENO_BIN="${DENO_BIN:-$(command -v deno 2>/dev/null || echo "$HOME/.deno/bin/deno")}"
[ -x "$DENO_BIN" ] || die "deno required — https://docs.deno.com/runtime/getting_started/installation/"

OPENCODE_BIN="${OPENCODE_BIN:-$(command -v opencode 2>/dev/null || echo "$HOME/.opencode/bin/opencode")}"
[ -x "$OPENCODE_BIN" ] || die "opencode required — https://opencode.ai"
# Iksir runs no models. It nestles at a humd and prompts whichever hive is
# kindled there. humd need not be running yet, but nothing works without one.
THRUM_SOCK="${HUM_THRUM_SOCK:-${HUM_SOCKET:-${XDG_STATE_HOME:-$HOME/.local/state}/hum/thrum.sock}}"
if [ ! -S "$THRUM_SOCK" ]; then
warn "no humd at $THRUM_SOCK"
warn "install one — https://github.com/adiled/hum — then kindle a worker hive"
warn "and something providing 'fs', or the murshid can think but not work."
fi
}

ensure_dirs() {
Expand Down Expand Up @@ -99,19 +103,15 @@ sync_config() {
fi
}

sync_opencode() {
local AGENT_DIR="$XDG_CONFIG_HOME/opencode/agent"
local PLUGIN_DIR="$XDG_CONFIG_HOME/opencode/plugins"
mkdir -p "$AGENT_DIR" "$PLUGIN_DIR"

# Symlink agent prompts (only iksir-* are OC agents, mayyaza-* are internal)
for file in "$IKSIR_SRC"/prompts/iksir-*.md; do
[ -f "$file" ] && ln -sf "$file" "$AGENT_DIR/$(basename "$file")"
sync_ruqan() {
# The ruqan are Iksir's own now — read from prompts/ and sent as the
# systemPrompt on each tone. No foreign config dir holds them.
local RUQAN_DIR="$IKSIR_CONFIG/prompts"
mkdir -p "$RUQAN_DIR"
for file in "$IKSIR_SRC"/prompts/*.md; do
[ -f "$file" ] && ln -sf "$file" "$RUQAN_DIR/$(basename "$file")"
done

# Symlink the plugin
ln -sf "$IKSIR_SRC/plugins/iksir.ts" "$PLUGIN_DIR/iksir.ts"
info "linked opencode agents and plugin"
info "linked ruqan"
}

install_cli() {
Expand Down Expand Up @@ -169,49 +169,11 @@ register_systemd() {
local WM
$IS_ROOT && WM="multi-user.target" || WM="default.target"

# MCP server
cat > "$SERVICE_DIR/iksir-mcp.service" <<EOF
[Unit]
Description=Iksir MCP Server
After=network.target

[Service]
Type=simple
WorkingDirectory=$IKSIR_SRC
ExecStart=$DENO_BIN run --allow-all --env=.env src/mcp/serve.ts --port=$MCP_PORT
Restart=on-failure
RestartSec=5
$ENV_VARS

[Install]
WantedBy=$WM
EOF

# Agent runtime
cat > "$SERVICE_DIR/iksir-agent.service" <<EOF
[Unit]
Description=Iksir Agent Runtime (OpenCode)
After=network.target iksir-mcp.service
Requires=iksir-mcp.service

[Service]
Type=simple
WorkingDirectory=$IKSIR_SRC
ExecStart=$OPENCODE_BIN serve --port $AGENT_PORT --hostname 0.0.0.0
Restart=on-failure
RestartSec=10
Environment=HOME=$HOME

[Install]
WantedBy=$WM
EOF

# Daemon
cat > "$SERVICE_DIR/iksir.service" <<EOF
[Unit]
Description=Iksir
After=network.target iksir-agent.service
Requires=iksir-agent.service
After=network.target

[Service]
Type=simple
Expand All @@ -226,15 +188,15 @@ WantedBy=$WM
EOF

$SC daemon-reload
$SC enable iksir-mcp iksir-agent iksir 2>/dev/null
$SC enable iksir 2>/dev/null
info "registered systemd services"
}

start_daemon() {
local SC="systemctl"
$IS_ROOT || SC="systemctl --user"

$SC restart iksir-mcp iksir-agent iksir
$SC restart iksir
sleep 2
if $SC is-active --quiet iksir; then
info "running"
Expand All @@ -246,7 +208,7 @@ start_daemon() {
stop_daemon() {
local SC="systemctl"
$IS_ROOT || SC="systemctl --user"
$SC stop iksir iksir-agent iksir-mcp 2>/dev/null || true
$SC stop iksir 2>/dev/null || true
}

# ─── commands ────────────────────────────────────────────────────────────────
Expand All @@ -261,10 +223,10 @@ print_status() {
echo ""
if $IS_ROOT; then
echo " logs: journalctl -u iksir -f"
echo " stop: systemctl stop iksir iksir-agent iksir-mcp"
echo " stop: systemctl stop iksir"
else
echo " logs: journalctl --user -u iksir -f"
echo " stop: systemctl --user stop iksir iksir-agent iksir-mcp"
echo " stop: systemctl --user stop iksir"
fi
echo ""
echo " First time? Edit $IKSIR_SRC/.env then: iksir start"
Expand All @@ -277,7 +239,7 @@ cmd_install() {
ensure_dirs
ensure_source
sync_config
sync_opencode
sync_ruqan
install_cli

info "registering services..."
Expand All @@ -296,7 +258,7 @@ cmd_update() {

stop_daemon
sync_config
sync_opencode
sync_ruqan
install_cli
register_services

Expand All @@ -319,19 +281,14 @@ cmd_uninstall() {
SERVICE_DIR="$HOME/.config/systemd/user"
fi

$SC disable iksir iksir-agent iksir-mcp 2>/dev/null || true
$SC disable iksir 2>/dev/null || true
rm -f "$SERVICE_DIR"/iksir*.service
$SC daemon-reload 2>/dev/null || true
info "removed systemd services"

rm -f "$BIN_DIR/iksir"
info "removed CLI"

# Remove opencode symlinks
local AGENT_DIR="$XDG_CONFIG_HOME/opencode/agent"
local PLUGIN_DIR="$XDG_CONFIG_HOME/opencode/plugins"
rm -f "$AGENT_DIR"/iksir-*.md "$PLUGIN_DIR/iksir.ts" 2>/dev/null || true
info "removed opencode symlinks"

echo ""
info "uninstalled. data left in place:"
Expand All @@ -354,10 +311,7 @@ case "$CMD" in
echo " uninstall — stop, remove services, remove CLI"
echo ""
echo "Environment:"
echo " IKSIR_AGENT_PORT Agent port (default: 4096)"
echo " IKSIR_MCP_PORT MCP port (default: 3100)"
echo " DENO_BIN Path to deno binary"
echo " OPENCODE_BIN Path to opencode binary"
exit 1
;;
esac
Loading