AI-driven zettelkasten mindmap
0
stars
172
commits
Python
primary language
Aug 29, 2026
updated
An AI-driven Zettelkasten-style mindmap and assistant for "talk to my data" and deep research over web-based resources.
This project is intended to be self-hosted with minimal infrastructure requirements - a mini PC with a multicore processor and 8+ GB RAM should suffice. Infrastructure components should be manageable by a 'compose' stack (note: though I'll be hosting on a k3s cluster). This means no GPU requirements; AI inference is provided through API services.
A Zettelkasten is a way of connecting atomic ideas into a linked (hypertextual), personal knowledge graph.
Each node must have:
xxHash might be used for an exact hash, minhash for textual similarity (i.e., similar words, letters), and/or semhash for semantic similarity.In a traditional Zettelkasten, the zettel body would contain links to other nodes. In the AI Zettelkasten, these are defined as an additional Relationship that contains source/destination directionality, relationship type, and other metadata.
Zettelkasten may also benefit from structural notes that create hierarchy, serving as aggregator or summary nodes about a broader (but still atomic!) concept that incorporates or relates to multiple, more granular nodes.
s3://aizk/db/ alongside conversion artifacts.This project uses Python 3.12+ and uv for dependency management.
To install, clone the repo, then run:
uv sync
Configuration is driven by environment variables and .env (auto-loaded from the repo root).
Required for API/worker:
AIZK_FETCHER__KARAKEEP__API_KEYAIZK_FETCHER__KARAKEEP__BASE_URLStorage (S3 or compatible):
AIZK_S3_BUCKET_NAME (default aizk)AIZK_S3_ENDPOINT_URL (required for MinIO/Garage or other S3-compatible endpoints)AIZK_S3_ACCESS_KEY_IDAIZK_S3_SECRET_ACCESS_KEYAIZK_S3_REGION (default us-east-1)Litestream (SQLite replication):
AIZK_LITESTREAM_ENABLED (default true)AIZK_LITESTREAM_START_ROLE (default api)AIZK_LITESTREAM_BINARY (default litestream)AIZK_LITESTREAM_CONFIG_PATH (default ./data/litestream.yaml)AIZK_LITESTREAM_S3_BUCKET_NAME (optional override; otherwise AIZK_S3_BUCKET_NAME)AIZK_LITESTREAM_S3_PREFIX (default db)AIZK_LITESTREAM_S3_FORCE_PATH_STYLE (default true)AIZK_LITESTREAM_S3_SIGN_PAYLOAD (default true)AIZK_LITESTREAM_RESTORE_ON_STARTUP (default true)AIZK_LITESTREAM_ALLOW_EMPTY_RESTORE (default true)MLflow tracing (optional):
AIZK_MLFLOW_TRACING_ENABLED (default false)AIZK_MLFLOW_TRACKING_URI (optional; uses MLflow defaults when unset)AIZK_MLFLOW_EXPERIMENT_NAME (optional)Deployment trust model:
AIZK_AUTH_MODE (default trust_network).
At this build the only implemented mode is trust_network, which treats every inbound request as authenticated and stamps a single deployment-wide principal on every row.
Reserved literal values token, proxy_headers, and oidc are rejected at startup with a typed ConfigurationError so the process refuses to boot rather than silently defaulting to an unintended trust posture.
Each future mode lands as a delta on the Principal.provenance literal, the AuthSettings.auth_mode validator, and the get_principal resolver match — no schema migration required.AIZK_DEFAULT_PRINCIPAL (default self).
In trust_network mode this string is the subject of every resolved Principal and therefore the owner_id written to sources, conversion_jobs, and conversion_outputs rows.
Override per deployment to attribute rows to a named operator or service account.AIZK_TRUSTED_HOSTS (default ["localhost", "127.0.0.1"], JSON array).
Allowlist for the Host header that reaches the API process; mismatches return HTTP 400 (Invalid host header) before any route handler runs.
Operators MUST override this for any non-localhost deployment.
The middleware checks the actual Host the API process sees — Forwarded and X-Forwarded-Host are intentionally NOT consulted, so the reverse proxy is responsible for (a) rewriting Host to a value in this allowlist on the way in and (b) stripping any client-supplied X-Forwarded-Host so it cannot reach the API.
Wildcards are supported, e.g. ["*.internal.example.com"].Docs: see docs/Litestream.md for full setup and sidecar guidance.
aizkRun the conversion CLI with uv:
uv run aizk-conversion db-init
AIZK_FETCHER__KARAKEEP__API_KEY=... AIZK_FETCHER__KARAKEEP__BASE_URL=... uv run aizk-conversion serve
AIZK_FETCHER__KARAKEEP__API_KEY=... AIZK_FETCHER__KARAKEEP__BASE_URL=... uv run aizk-conversion worker
To backfill or re-enqueue existing KaraKeep bookmarks, use the notebook at notebooks/karakeep_conversion_pipeline.py.
It pages through KaraKeep and submits bookmark IDs to the conversion API.
The notebook includes required env vars, startup commands for API/worker, and a KARAKEEP_DRY_RUN mode for verification.
This repo uses nix devshells to manage project dependencies.
Use node2nix to create node-env.nix from package.json node-env.nix will be picked up in the flake devshell
node2nix -i package.json -o ./nix/node-packages.nix -c ./nix/default.nix -e ./nix/node-env.nix -18
Use the Podman compose file to run API + worker separately from the same image:
podman-compose -f containers/podman-compose.yaml up -d --build
This project uses spec-driven development to allow AI coding assistance to work on well-specified features.
See the sdd-* family of ahgraber/skills: Agent skills.
Run tests with uv:
uv run pytest tests/
Run tests in parallel across CPU cores using pytest-xdist:
uv run pytest -n auto -m "not integration_lifecycle" tests/
Subprocess lifecycle tests (integration_lifecycle) are incompatible with xdist and must be run separately:
uv run pytest -m integration_lifecycle tests/
With coverage:
uv run pytest -n auto -m "not integration_lifecycle" --cov=src --cov-report=term-missing tests/
Contributions and fixes are welcome. Please open issues or pull requests with clear descriptions and tests where appropriate.
This project uses uv-ship to manage releases. Install it as a uv tool:
uv tool install uv-ship
To cut a release:
# do a dry run first!
uv-ship --dry-run next <major | minor | patch>
# if everything looks good, ship it
uv-ship next <major | minor | patch>
This bumps the version in pyproject.toml, updates CHANGELOG, commits, tags, and pushes.
See [tool.uv-ship] in pyproject.toml for configuration.
172 commits
Python
92.0%
Nix
6.6%
HTML
1.1%
AI-driven zettelkasten mindmap
0
stars
172
commits
Python
primary language
Aug 29, 2026
updated
An AI-driven Zettelkasten-style mindmap and assistant for "talk to my data" and deep research over web-based resources.
This project is intended to be self-hosted with minimal infrastructure requirements - a mini PC with a multicore processor and 8+ GB RAM should suffice. Infrastructure components should be manageable by a 'compose' stack (note: though I'll be hosting on a k3s cluster). This means no GPU requirements; AI inference is provided through API services.
A Zettelkasten is a way of connecting atomic ideas into a linked (hypertextual), personal knowledge graph.
Each node must have:
xxHash might be used for an exact hash, minhash for textual similarity (i.e., similar words, letters), and/or semhash for semantic similarity.In a traditional Zettelkasten, the zettel body would contain links to other nodes. In the AI Zettelkasten, these are defined as an additional Relationship that contains source/destination directionality, relationship type, and other metadata.
Zettelkasten may also benefit from structural notes that create hierarchy, serving as aggregator or summary nodes about a broader (but still atomic!) concept that incorporates or relates to multiple, more granular nodes.
s3://aizk/db/ alongside conversion artifacts.This project uses Python 3.12+ and uv for dependency management.
To install, clone the repo, then run:
uv sync
Configuration is driven by environment variables and .env (auto-loaded from the repo root).
Required for API/worker:
AIZK_FETCHER__KARAKEEP__API_KEYAIZK_FETCHER__KARAKEEP__BASE_URLStorage (S3 or compatible):
AIZK_S3_BUCKET_NAME (default aizk)AIZK_S3_ENDPOINT_URL (required for MinIO/Garage or other S3-compatible endpoints)AIZK_S3_ACCESS_KEY_IDAIZK_S3_SECRET_ACCESS_KEYAIZK_S3_REGION (default us-east-1)Litestream (SQLite replication):
AIZK_LITESTREAM_ENABLED (default true)AIZK_LITESTREAM_START_ROLE (default api)AIZK_LITESTREAM_BINARY (default litestream)AIZK_LITESTREAM_CONFIG_PATH (default ./data/litestream.yaml)AIZK_LITESTREAM_S3_BUCKET_NAME (optional override; otherwise AIZK_S3_BUCKET_NAME)AIZK_LITESTREAM_S3_PREFIX (default db)AIZK_LITESTREAM_S3_FORCE_PATH_STYLE (default true)AIZK_LITESTREAM_S3_SIGN_PAYLOAD (default true)AIZK_LITESTREAM_RESTORE_ON_STARTUP (default true)AIZK_LITESTREAM_ALLOW_EMPTY_RESTORE (default true)MLflow tracing (optional):
AIZK_MLFLOW_TRACING_ENABLED (default false)AIZK_MLFLOW_TRACKING_URI (optional; uses MLflow defaults when unset)AIZK_MLFLOW_EXPERIMENT_NAME (optional)Deployment trust model:
AIZK_AUTH_MODE (default trust_network).
At this build the only implemented mode is trust_network, which treats every inbound request as authenticated and stamps a single deployment-wide principal on every row.
Reserved literal values token, proxy_headers, and oidc are rejected at startup with a typed ConfigurationError so the process refuses to boot rather than silently defaulting to an unintended trust posture.
Each future mode lands as a delta on the Principal.provenance literal, the AuthSettings.auth_mode validator, and the get_principal resolver match — no schema migration required.AIZK_DEFAULT_PRINCIPAL (default self).
In trust_network mode this string is the subject of every resolved Principal and therefore the owner_id written to sources, conversion_jobs, and conversion_outputs rows.
Override per deployment to attribute rows to a named operator or service account.AIZK_TRUSTED_HOSTS (default ["localhost", "127.0.0.1"], JSON array).
Allowlist for the Host header that reaches the API process; mismatches return HTTP 400 (Invalid host header) before any route handler runs.
Operators MUST override this for any non-localhost deployment.
The middleware checks the actual Host the API process sees — Forwarded and X-Forwarded-Host are intentionally NOT consulted, so the reverse proxy is responsible for (a) rewriting Host to a value in this allowlist on the way in and (b) stripping any client-supplied X-Forwarded-Host so it cannot reach the API.
Wildcards are supported, e.g. ["*.internal.example.com"].Docs: see docs/Litestream.md for full setup and sidecar guidance.
aizkRun the conversion CLI with uv:
uv run aizk-conversion db-init
AIZK_FETCHER__KARAKEEP__API_KEY=... AIZK_FETCHER__KARAKEEP__BASE_URL=... uv run aizk-conversion serve
AIZK_FETCHER__KARAKEEP__API_KEY=... AIZK_FETCHER__KARAKEEP__BASE_URL=... uv run aizk-conversion worker
To backfill or re-enqueue existing KaraKeep bookmarks, use the notebook at notebooks/karakeep_conversion_pipeline.py.
It pages through KaraKeep and submits bookmark IDs to the conversion API.
The notebook includes required env vars, startup commands for API/worker, and a KARAKEEP_DRY_RUN mode for verification.
This repo uses nix devshells to manage project dependencies.
Use node2nix to create node-env.nix from package.json node-env.nix will be picked up in the flake devshell
node2nix -i package.json -o ./nix/node-packages.nix -c ./nix/default.nix -e ./nix/node-env.nix -18
Use the Podman compose file to run API + worker separately from the same image:
podman-compose -f containers/podman-compose.yaml up -d --build
This project uses spec-driven development to allow AI coding assistance to work on well-specified features.
See the sdd-* family of ahgraber/skills: Agent skills.
Run tests with uv:
uv run pytest tests/
Run tests in parallel across CPU cores using pytest-xdist:
uv run pytest -n auto -m "not integration_lifecycle" tests/
Subprocess lifecycle tests (integration_lifecycle) are incompatible with xdist and must be run separately:
uv run pytest -m integration_lifecycle tests/
With coverage:
uv run pytest -n auto -m "not integration_lifecycle" --cov=src --cov-report=term-missing tests/
Contributions and fixes are welcome. Please open issues or pull requests with clear descriptions and tests where appropriate.
This project uses uv-ship to manage releases. Install it as a uv tool:
uv tool install uv-ship
To cut a release:
# do a dry run first!
uv-ship --dry-run next <major | minor | patch>
# if everything looks good, ship it
uv-ship next <major | minor | patch>
This bumps the version in pyproject.toml, updates CHANGELOG, commits, tags, and pushes.
See [tool.uv-ship] in pyproject.toml for configuration.
172 commits
Python
92.0%
Nix
6.6%
HTML
1.1%