various updates
This commit is contained in:
@@ -42,6 +42,7 @@ or give me access to your servers.
|
||||
- [bash](#bash)
|
||||
- [Bulk File/Folder Renaming](#bulk-filefolder-renaming)
|
||||
- [Escaping a Stuck SSH Terminal](#escaping-a-stuck-ssh-terminal)
|
||||
- [Curl output hidden by bash prompt](#curl-output-hidden-by-bash-prompt)
|
||||
- [SSH Setup](#ssh-setup)
|
||||
- [Git GPG Commit Signing](#git-gpg-commit-signing)
|
||||
- [Important Dates and Times](#important-dates-and-times)
|
||||
@@ -133,6 +134,14 @@ done
|
||||
|
||||
Press the following keys: enter + ~ + .
|
||||
|
||||
### Curl output hidden by bash prompt
|
||||
|
||||
Add this to your ~/.curlrc
|
||||
|
||||
```conf
|
||||
-w "\n"
|
||||
```
|
||||
|
||||
## SSH Setup
|
||||
|
||||
Generate a key (password protect it!)
|
||||
@@ -212,15 +221,16 @@ signed you'll see an output. If not, nothing will show.
|
||||
|
||||
## Important Dates and Times
|
||||
|
||||
| Time | Day | Description |
|
||||
| ----- | --- | ---------------------------------- |
|
||||
| 00:00 | All | Automated builds |
|
||||
| 00:00 | All | NAS Snapshots |
|
||||
| 02:00 | All | Backups |
|
||||
| 04:00 | All | Bare Metal Server Security Updates |
|
||||
| 05:00 | All | VM Server Security Updates |
|
||||
| 05:00 | All | Unifi Protect Firmware Updates |
|
||||
| 06:00 | All | Unifi Network Firmware Updates |
|
||||
| Time | Day | Description |
|
||||
| ----- | --- | ------------------------------ |
|
||||
| 00:00 | All | Automated builds |
|
||||
| 00:00 | All | NAS Snapshots |
|
||||
| 01:00 | All | Backups |
|
||||
| 03:00 | All | App Updates |
|
||||
| 04:00 | All | Bare Metal Server Updates |
|
||||
| 05:00 | All | VM Server Updates |
|
||||
| 05:00 | All | Unifi Protect Firmware Updates |
|
||||
| 06:00 | All | Unifi Network Firmware Updates |
|
||||
|
||||
## Project Lifecycle
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# AWS Bedrock
|
||||
|
||||
## Endpoints
|
||||
|
||||
Completions endpoint: <https://bedrock-mantle.us-east-1.api.aws/v1>
|
||||
|
||||
## Model Analysis
|
||||
|
||||
| Model ID | Input Price (Mil) | Output Price (Mil) | Doodle Jump Notes |
|
||||
| ----------------------- | ----------------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| minimax.minimax-m2.5 | $ 0.30 | $ 1.20 | Way too fast and player moves too slow. Identified issue and fixed after second attempt |
|
||||
| zai.glm-5 | $ 1.00 | $ 3.20 | Perfect game with great graphics. |
|
||||
| mistral.devstral-2-123b | $ 0.40 | $ 2.00 | Broken game with very basic graphics. Second attempt broken as well. |
|
||||
| deepseek.v3.2 | $1.35 | $5.40 | Way too fast, platform generation erratic, mobile controls were a nice touch. Fixed speed issue on second attempt but platform generation still bad |
|
||||
@@ -1,48 +0,0 @@
|
||||
services:
|
||||
gitea:
|
||||
image: docker.gitea.com/gitea:1.25-rootless
|
||||
container_name: gitea
|
||||
environment:
|
||||
- GITEA__database__DB_TYPE=postgres
|
||||
- GITEA__database__HOST=postgres:5432
|
||||
- GITEA__database__NAME=gitea
|
||||
- GITEA__database__USER=gitea
|
||||
- GITEA__database__PASSWD=gitea
|
||||
security_opt:
|
||||
- "label=disable"
|
||||
restart: always
|
||||
networks:
|
||||
- gitea
|
||||
volumes:
|
||||
- /home/gitea/gitea_data:/data:Z
|
||||
- /home/gitea/gitea_etc:/etc/gitea:Z
|
||||
- /home/gitea/gitea_custom:/var/lib/gitea/custom:Z
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
ports:
|
||||
- "3000:3000"
|
||||
- "2222:2222"
|
||||
depends_on:
|
||||
- postgres
|
||||
labels:
|
||||
- "io.containers.autoupdate=registry"
|
||||
|
||||
postgres:
|
||||
image: docker.io/library/postgres:15
|
||||
container_name: postgres
|
||||
security_opt:
|
||||
- "label=disable"
|
||||
restart: always
|
||||
environment:
|
||||
- POSTGRES_USER=gitea
|
||||
- POSTGRES_PASSWORD=gitea
|
||||
- POSTGRES_DB=gitea
|
||||
networks:
|
||||
- gitea
|
||||
volumes:
|
||||
- /home/gitea/gitea_postgres:/var/lib/postgresql/data:Z
|
||||
labels:
|
||||
- "io.containers.autoupdate=registry"
|
||||
|
||||
networks:
|
||||
gitea:
|
||||
enable_ipv6: true
|
||||
@@ -4,7 +4,7 @@ networks:
|
||||
|
||||
services:
|
||||
server:
|
||||
image: docker.gitea.com/gitea:1.25.3
|
||||
image: docker.gitea.com/gitea:1.26.1
|
||||
container_name: gitea
|
||||
environment:
|
||||
- USER_UID=1001
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
[Unit]
|
||||
Requires=postgres.service
|
||||
|
||||
[Container]
|
||||
AutoUpdate=registry
|
||||
ContainerName=gitea
|
||||
Environment=GITEA__database__DB_TYPE=postgres GITEA__database__HOST=postgres:5432 GITEA__database__NAME=gitea GITEA__database__USER=gitea GITEA__database__PASSWD=gitea
|
||||
Image=docker.gitea.com/gitea:1.25-rootless
|
||||
Network=gitea.network
|
||||
PublishPort=3000:3000
|
||||
PublishPort=2222:2222
|
||||
SecurityLabelDisable=true
|
||||
Volume=/home/gitea/gitea_data:/data:Z
|
||||
Volume=/home/gitea/gitea_etc:/etc/gitea:Z
|
||||
Volume=/home/gitea/gitea_custom:/var/lib/gitea/custom:Z
|
||||
Volume=/etc/localtime:/etc/localtime:ro
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -1,14 +0,0 @@
|
||||
[Container]
|
||||
AutoUpdate=registry
|
||||
ContainerName=postgres
|
||||
Environment=POSTGRES_USER=gitea POSTGRES_PASSWORD=gitea POSTGRES_DB=gitea
|
||||
Image=docker.io/library/postgres:15
|
||||
Network=gitea.network
|
||||
SecurityLabelDisable=true
|
||||
Volume=/home/gitea/gitea_postgres:/var/lib/postgresql/data:Z
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -10,7 +10,7 @@ DB_DATA_LOCATION=/srv/immich-db/postgres
|
||||
# TZ=Etc/UTC
|
||||
|
||||
# The Immich version to use. You can pin this to a specific version like "v2.1.0"
|
||||
IMMICH_VERSION=v2
|
||||
IMMICH_VERSION=v3
|
||||
|
||||
# Connection secret for postgres. You should change it to a random password
|
||||
# Please use only the characters `A-Za-z0-9`, without special characters or spaces
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
services:
|
||||
keycloak:
|
||||
container_name: keycloak
|
||||
restart: always
|
||||
image: quay.io/keycloak/keycloak:26.4.5
|
||||
ports:
|
||||
- "9443:443"
|
||||
- "8443:8443"
|
||||
volumes:
|
||||
- /home/foobar/data:/var/app/data
|
||||
security_opt:
|
||||
- label=disable
|
||||
userns_mode: keep-id
|
||||
command:
|
||||
- "start"
|
||||
- "--hostname"
|
||||
- "https://keycloak.reeseapps.com"
|
||||
- "--hostname-admin"
|
||||
- "https://keycloak.reeselink.com:8443"
|
||||
@@ -1,186 +1,10 @@
|
||||
# Podman keycloak
|
||||
|
||||
- [Podman keycloak](#podman-keycloak)
|
||||
- [Setup keycloak Project](#setup-keycloak-project)
|
||||
- [Install Keycloak with Docker](#install-keycloak-with-docker)
|
||||
- [Install Keycloak with Podman](#install-keycloak-with-podman)
|
||||
- [Create the keycloak user](#create-the-keycloak-user)
|
||||
- [Write the keycloak compose spec](#write-the-keycloak-compose-spec)
|
||||
- [A Note on Volumes](#a-note-on-volumes)
|
||||
- [Convert keycloak compose spec to quadlets](#convert-keycloak-compose-spec-to-quadlets)
|
||||
- [Create any container-mounted directories](#create-any-container-mounted-directories)
|
||||
- [Start and enable your systemd quadlet](#start-and-enable-your-systemd-quadlet)
|
||||
- [Expose keycloak](#expose-keycloak)
|
||||
- [firewalld](#firewalld)
|
||||
- [Backup keycloak](#backup-keycloak)
|
||||
- [Upgrade keycloak](#upgrade-keycloak)
|
||||
- [Upgrade Quadlets](#upgrade-quadlets)
|
||||
- [Uninstall](#uninstall)
|
||||
- [Notes](#notes)
|
||||
- [SELinux](#selinux)
|
||||
- [Adding a Client](#adding-a-client)
|
||||
|
||||
## Setup keycloak Project
|
||||
## Adding a Client
|
||||
|
||||
- [ ] Copy and rename this folder to active/container_keycloak
|
||||
- [ ] Find and replace keycloak with the name of the service.
|
||||
- [ ] Create the rootless user to run the podman containers
|
||||
- [ ] Write the compose.yaml spec for your service
|
||||
- [ ] Convert the compose.yaml spec to a quadlet
|
||||
- [ ] Install the quadlet on the podman server
|
||||
- [ ] Expose the quadlet service
|
||||
- [ ] Install a backup service and timer
|
||||
|
||||
## Install Keycloak with Docker
|
||||
|
||||
<https://www.keycloak.org/getting-started/getting-started-docker>
|
||||
|
||||
```bash
|
||||
# Test in dev mode
|
||||
docker run -p 8080:8080 -e KC_BOOTSTRAP_ADMIN_USERNAME=admin -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:26.4.7 start-dev
|
||||
```
|
||||
|
||||
## Install Keycloak with Podman
|
||||
|
||||
### Create the keycloak user
|
||||
|
||||
```bash
|
||||
# SSH into your podman server as root
|
||||
useradd keycloak
|
||||
loginctl enable-linger $(id -u keycloak)
|
||||
systemctl --user --machine=keycloak@.host enable podman-restart
|
||||
systemctl --user --machine=keycloak@.host enable --now podman.socket
|
||||
mkdir -p /home/keycloak/.config/containers/systemd
|
||||
```
|
||||
|
||||
### Write the keycloak compose spec
|
||||
|
||||
<https://www.keycloak.org/getting-started/getting-started-podman>
|
||||
|
||||
```bash
|
||||
# Based on the example
|
||||
podman run \
|
||||
-p 127.0.0.1:8080:8080 \
|
||||
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
|
||||
-e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
|
||||
quay.io/keycloak/keycloak:26.4.5 start-dev
|
||||
```
|
||||
|
||||
#### A Note on Volumes
|
||||
|
||||
Named volumes are stored at `/home/keycloak/.local/share/containers/storage/volumes/`.
|
||||
|
||||
### Convert keycloak compose spec to quadlets
|
||||
|
||||
Run the following to convert a compose.yaml into the various `.container` files for systemd:
|
||||
|
||||
```bash
|
||||
# Generate the systemd service
|
||||
podman run \
|
||||
--security-opt label=disable \
|
||||
--rm \
|
||||
-v $(pwd)/active/container_keycloak/:/compose \
|
||||
-v $(pwd)/active/container_keycloak/quadlets:/quadlets \
|
||||
quay.io/k9withabone/podlet \
|
||||
-f /quadlets \
|
||||
-i \
|
||||
--overwrite \
|
||||
compose /compose/compose.yaml
|
||||
|
||||
# Copy the files to the server
|
||||
export PODMAN_SERVER=
|
||||
scp -r active/container_keycloak/quadlets/. $PODMAN_SERVER:/home/keycloak/.config/containers/systemd/
|
||||
ssh $PODMAN_SERVER chown -R keycloak:keycloak /home/keycloak/.config/containers/systemd/
|
||||
```
|
||||
|
||||
### Create any container-mounted directories
|
||||
|
||||
SSH into your podman server as root:
|
||||
|
||||
```bash
|
||||
machinectl shell keycloak@
|
||||
podman unshare
|
||||
mkdir some_volume
|
||||
# Chown to the namespaced user with UID 1000
|
||||
# This will be some really obscure UID outside the namespace
|
||||
# This will also solve most permission denied errors
|
||||
chown -R 1000:1000 some_volume
|
||||
```
|
||||
|
||||
### Start and enable your systemd quadlet
|
||||
|
||||
SSH into your podman server as root:
|
||||
|
||||
```bash
|
||||
machinectl shell keycloak@
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user restart keycloak
|
||||
# Enable auto-update service which will pull new container images automatically every day
|
||||
systemctl --user enable --now podman-auto-update.timer
|
||||
```
|
||||
|
||||
### Expose keycloak
|
||||
|
||||
1. If you need a domain, follow the [DDNS instructions](/active/container_ddns/ddns.md#install-a-new-ddns-service)
|
||||
2. For a web service, follow the [Caddy instructions](/active/container_caddy/caddy.md#adding-a-new-caddy-record)
|
||||
3. Finally, follow your OS's guide for opening ports via its firewall service.
|
||||
|
||||
#### firewalld
|
||||
|
||||
```bash
|
||||
# command to get current active zone and default zone
|
||||
firewall-cmd --get-active-zones
|
||||
firewall-cmd --get-default-zone
|
||||
|
||||
# command to open 443 on tcp
|
||||
firewall-cmd --permanent --zone=<zone> --add-port=443/tcp
|
||||
|
||||
# command to open 80 and 443 on tcp and udp
|
||||
firewall-cmd --permanent --zone=<zone> --add-port={80,443}/{tcp,udp}
|
||||
|
||||
# command to list available services and then open http and https
|
||||
firewall-cmd --get-services
|
||||
firewall-cmd --permanent --zone=<zone> --add-service={http,https}
|
||||
```
|
||||
|
||||
## Backup keycloak
|
||||
|
||||
Follow the [Borg Backup instructions](/active/systemd_borg/borg.md#set-up-a-client-for-backup)
|
||||
|
||||
## Upgrade keycloak
|
||||
|
||||
### Upgrade Quadlets
|
||||
|
||||
Upgrades should be a repeat of [writing the compose spec](#convert-keycloak-compose-spec-to-quadlets) and [installing the quadlets](#start-and-enable-your-systemd-quadlet)
|
||||
|
||||
```bash
|
||||
export PODMAN_SERVER=
|
||||
scp -r quadlets/. $PODMAN_SERVER$:/home/keycloak/.config/containers/systemd/
|
||||
ssh keycloak systemctl --user daemon-reload
|
||||
ssh keycloak systemctl --user restart keycloak
|
||||
```
|
||||
|
||||
## Uninstall
|
||||
|
||||
```bash
|
||||
# Stop the user's services
|
||||
systemctl --user disable podman-restart
|
||||
podman container stop --all
|
||||
systemctl --user disable --now podman.socket
|
||||
systemctl --user disable --now podman-auto-update.timer
|
||||
|
||||
# Delete the user (this won't delete their home directory)
|
||||
# userdel might spit out an error like:
|
||||
# userdel: user keycloak is currently used by process 591255
|
||||
# kill those processes and try again
|
||||
userdel keycloak
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
### SELinux
|
||||
|
||||
<https://blog.christophersmart.com/2021/01/31/podman-volumes-and-selinux/>
|
||||
|
||||
:z allows a container to share a mounted volume with all other containers.
|
||||
|
||||
:Z allows a container to reserve a mounted volume and prevents any other container from accessing.
|
||||
1. Create a new client
|
||||
2. Clients -> click on client -> Client scopes -> client-frontend-dedicated -> Scope -> turn off Full Scope Allowed
|
||||
3.
|
||||
@@ -1,3 +0,0 @@
|
||||
# Quadlets
|
||||
|
||||
Put your quadlets here.
|
||||
@@ -0,0 +1,168 @@
|
||||
# Make sure to update the credential placeholders with your own secrets.
|
||||
# We mark them with # CHANGEME in the file below.
|
||||
# In addition, we recommend to restrict inbound traffic on the host to langfuse-web (port 3000) and minio (port 9090) only.
|
||||
# All other components are bound to localhost (127.0.0.1) to only accept connections from the local machine.
|
||||
# External connections from other machines will not be able to reach these services directly.
|
||||
services:
|
||||
langfuse-worker:
|
||||
image: docker.io/langfuse/langfuse-worker:3
|
||||
restart: always
|
||||
ports:
|
||||
- 127.0.0.1:3030:3030
|
||||
environment: &langfuse-worker-env
|
||||
NEXTAUTH_URL: ${NEXTAUTH_URL:-http://localhost:3000}
|
||||
DATABASE_URL: ${DATABASE_URL:-postgresql://postgres:postgres@postgres:5432/postgres} # CHANGEME
|
||||
SALT: ${SALT:-mysalt} # CHANGEME
|
||||
ENCRYPTION_KEY: ${ENCRYPTION_KEY:-0000000000000000000000000000000000000000000000000000000000000000} # CHANGEME: generate via `openssl rand -hex 32`
|
||||
TELEMETRY_ENABLED: ${TELEMETRY_ENABLED:-true}
|
||||
LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES: ${LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES:-false}
|
||||
CLICKHOUSE_MIGRATION_URL: ${CLICKHOUSE_MIGRATION_URL:-clickhouse://clickhouse:9000}
|
||||
CLICKHOUSE_URL: ${CLICKHOUSE_URL:-http://clickhouse:8123}
|
||||
CLICKHOUSE_USER: ${CLICKHOUSE_USER:-clickhouse}
|
||||
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:-clickhouse} # CHANGEME
|
||||
CLICKHOUSE_CLUSTER_ENABLED: ${CLICKHOUSE_CLUSTER_ENABLED:-false}
|
||||
LANGFUSE_USE_AZURE_BLOB: ${LANGFUSE_USE_AZURE_BLOB:-false}
|
||||
LANGFUSE_USE_OCI_NATIVE_OBJECT_STORAGE: ${LANGFUSE_USE_OCI_NATIVE_OBJECT_STORAGE:-false}
|
||||
LANGFUSE_OCI_AUTH_TYPE: ${LANGFUSE_OCI_AUTH_TYPE:-workload_identity}
|
||||
LANGFUSE_S3_EVENT_UPLOAD_BUCKET: ${LANGFUSE_S3_EVENT_UPLOAD_BUCKET:-langfuse}
|
||||
LANGFUSE_S3_EVENT_UPLOAD_REGION: ${LANGFUSE_S3_EVENT_UPLOAD_REGION:-auto}
|
||||
LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID: ${LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID:-minio}
|
||||
LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY: ${LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY:-miniosecret} # CHANGEME
|
||||
LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT: ${LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT:-http://minio:9000}
|
||||
LANGFUSE_S3_EVENT_UPLOAD_FORCE_PATH_STYLE: ${LANGFUSE_S3_EVENT_UPLOAD_FORCE_PATH_STYLE:-true}
|
||||
LANGFUSE_S3_EVENT_UPLOAD_PREFIX: ${LANGFUSE_S3_EVENT_UPLOAD_PREFIX:-events/}
|
||||
LANGFUSE_S3_MEDIA_UPLOAD_BUCKET: ${LANGFUSE_S3_MEDIA_UPLOAD_BUCKET:-langfuse}
|
||||
LANGFUSE_S3_MEDIA_UPLOAD_REGION: ${LANGFUSE_S3_MEDIA_UPLOAD_REGION:-auto}
|
||||
LANGFUSE_S3_MEDIA_UPLOAD_ACCESS_KEY_ID: ${LANGFUSE_S3_MEDIA_UPLOAD_ACCESS_KEY_ID:-minio}
|
||||
LANGFUSE_S3_MEDIA_UPLOAD_SECRET_ACCESS_KEY: ${LANGFUSE_S3_MEDIA_UPLOAD_SECRET_ACCESS_KEY:-miniosecret} # CHANGEME
|
||||
LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT: ${LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT:-http://localhost:9090}
|
||||
LANGFUSE_S3_MEDIA_UPLOAD_FORCE_PATH_STYLE: ${LANGFUSE_S3_MEDIA_UPLOAD_FORCE_PATH_STYLE:-true}
|
||||
LANGFUSE_S3_MEDIA_UPLOAD_PREFIX: ${LANGFUSE_S3_MEDIA_UPLOAD_PREFIX:-media/}
|
||||
LANGFUSE_S3_BATCH_EXPORT_ENABLED: ${LANGFUSE_S3_BATCH_EXPORT_ENABLED:-false}
|
||||
LANGFUSE_S3_BATCH_EXPORT_BUCKET: ${LANGFUSE_S3_BATCH_EXPORT_BUCKET:-langfuse}
|
||||
LANGFUSE_S3_BATCH_EXPORT_PREFIX: ${LANGFUSE_S3_BATCH_EXPORT_PREFIX:-exports/}
|
||||
LANGFUSE_S3_BATCH_EXPORT_REGION: ${LANGFUSE_S3_BATCH_EXPORT_REGION:-auto}
|
||||
LANGFUSE_S3_BATCH_EXPORT_ENDPOINT: ${LANGFUSE_S3_BATCH_EXPORT_ENDPOINT:-http://minio:9000}
|
||||
LANGFUSE_S3_BATCH_EXPORT_EXTERNAL_ENDPOINT: ${LANGFUSE_S3_BATCH_EXPORT_EXTERNAL_ENDPOINT:-http://localhost:9090}
|
||||
LANGFUSE_S3_BATCH_EXPORT_ACCESS_KEY_ID: ${LANGFUSE_S3_BATCH_EXPORT_ACCESS_KEY_ID:-minio}
|
||||
LANGFUSE_S3_BATCH_EXPORT_SECRET_ACCESS_KEY: ${LANGFUSE_S3_BATCH_EXPORT_SECRET_ACCESS_KEY:-miniosecret} # CHANGEME
|
||||
LANGFUSE_S3_BATCH_EXPORT_FORCE_PATH_STYLE: ${LANGFUSE_S3_BATCH_EXPORT_FORCE_PATH_STYLE:-true}
|
||||
LANGFUSE_INGESTION_QUEUE_DELAY_MS: ${LANGFUSE_INGESTION_QUEUE_DELAY_MS:-}
|
||||
LANGFUSE_INGESTION_CLICKHOUSE_WRITE_INTERVAL_MS: ${LANGFUSE_INGESTION_CLICKHOUSE_WRITE_INTERVAL_MS:-}
|
||||
REDIS_HOST: ${REDIS_HOST:-redis}
|
||||
REDIS_PORT: ${REDIS_PORT:-6379}
|
||||
REDIS_AUTH: ${REDIS_AUTH:-myredissecret} # CHANGEME
|
||||
REDIS_TLS_ENABLED: ${REDIS_TLS_ENABLED:-false}
|
||||
REDIS_TLS_CA: ${REDIS_TLS_CA:-/certs/ca.crt}
|
||||
REDIS_TLS_CERT: ${REDIS_TLS_CERT:-/certs/redis.crt}
|
||||
REDIS_TLS_KEY: ${REDIS_TLS_KEY:-/certs/redis.key}
|
||||
EMAIL_FROM_ADDRESS: ${EMAIL_FROM_ADDRESS:-}
|
||||
SMTP_CONNECTION_URL: ${SMTP_CONNECTION_URL:-}
|
||||
|
||||
langfuse-web:
|
||||
image: docker.io/langfuse/langfuse:3
|
||||
restart: always
|
||||
ports:
|
||||
- 3000:3000
|
||||
environment:
|
||||
<<: *langfuse-worker-env
|
||||
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET:-mysecret} # CHANGEME
|
||||
LANGFUSE_INIT_ORG_ID: ${LANGFUSE_INIT_ORG_ID:-}
|
||||
LANGFUSE_INIT_ORG_NAME: ${LANGFUSE_INIT_ORG_NAME:-}
|
||||
LANGFUSE_INIT_PROJECT_ID: ${LANGFUSE_INIT_PROJECT_ID:-}
|
||||
LANGFUSE_INIT_PROJECT_NAME: ${LANGFUSE_INIT_PROJECT_NAME:-}
|
||||
LANGFUSE_INIT_PROJECT_PUBLIC_KEY: ${LANGFUSE_INIT_PROJECT_PUBLIC_KEY:-}
|
||||
LANGFUSE_INIT_PROJECT_SECRET_KEY: ${LANGFUSE_INIT_PROJECT_SECRET_KEY:-}
|
||||
LANGFUSE_INIT_USER_EMAIL: ${LANGFUSE_INIT_USER_EMAIL:-admin@ducoterra.net}
|
||||
LANGFUSE_INIT_USER_NAME: ${LANGFUSE_INIT_USER_NAME:-admin}
|
||||
LANGFUSE_INIT_USER_PASSWORD: ${LANGFUSE_INIT_USER_PASSWORD:-testpass}
|
||||
|
||||
clickhouse:
|
||||
image: docker.io/clickhouse/clickhouse-server
|
||||
restart: always
|
||||
user: "101:101"
|
||||
environment:
|
||||
CLICKHOUSE_DB: default
|
||||
CLICKHOUSE_USER: ${CLICKHOUSE_USER:-clickhouse}
|
||||
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:-clickhouse} # CHANGEME
|
||||
volumes:
|
||||
- langfuse_clickhouse_data:/var/lib/clickhouse
|
||||
- langfuse_clickhouse_logs:/var/log/clickhouse-server
|
||||
ports:
|
||||
- 127.0.0.1:8123:8123
|
||||
- 127.0.0.1:9000:9000
|
||||
healthcheck:
|
||||
test: wget --no-verbose --tries=1 --spider http://localhost:8123/ping || exit 1
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
start_period: 1s
|
||||
|
||||
minio:
|
||||
image: cgr.dev/chainguard/minio
|
||||
restart: always
|
||||
entrypoint: sh
|
||||
# create the 'langfuse' bucket before starting the service
|
||||
command: -c 'mkdir -p /data/langfuse && minio server --address ":9000" --console-address ":9001" /data'
|
||||
environment:
|
||||
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minio}
|
||||
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-miniosecret} # CHANGEME
|
||||
ports:
|
||||
- 9090:9000
|
||||
- 127.0.0.1:9091:9001
|
||||
volumes:
|
||||
- langfuse_minio_data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "mc", "ready", "local"]
|
||||
interval: 1s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 1s
|
||||
|
||||
redis:
|
||||
image: docker.io/redis:7
|
||||
restart: always
|
||||
# CHANGEME: row below to secure redis password
|
||||
command: >
|
||||
--requirepass ${REDIS_AUTH:-myredissecret}
|
||||
--maxmemory-policy noeviction
|
||||
ports:
|
||||
- 127.0.0.1:6379:6379
|
||||
volumes:
|
||||
- langfuse_redis_data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 3s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
|
||||
postgres:
|
||||
image: docker.io/postgres:${POSTGRES_VERSION:-17}
|
||||
restart: always
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 3s
|
||||
timeout: 3s
|
||||
retries: 10
|
||||
environment:
|
||||
POSTGRES_USER: ${POSTGRES_USER:-postgres}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres} # CHANGEME
|
||||
POSTGRES_DB: ${POSTGRES_DB:-postgres}
|
||||
TZ: UTC
|
||||
PGTZ: UTC
|
||||
ports:
|
||||
- 127.0.0.1:5432:5432
|
||||
volumes:
|
||||
- langfuse_postgres_data:/var/lib/postgresql/data
|
||||
|
||||
volumes:
|
||||
langfuse_postgres_data:
|
||||
driver: local
|
||||
langfuse_clickhouse_data:
|
||||
driver: local
|
||||
langfuse_clickhouse_logs:
|
||||
driver: local
|
||||
langfuse_minio_data:
|
||||
driver: local
|
||||
langfuse_redis_data:
|
||||
driver: local
|
||||
@@ -0,0 +1,167 @@
|
||||
# Podman langfuse
|
||||
|
||||
- [Podman langfuse](#podman-langfuse)
|
||||
- [Setup langfuse Project](#setup-langfuse-project)
|
||||
- [Install langfuse](#install-langfuse)
|
||||
- [Create the langfuse user](#create-the-langfuse-user)
|
||||
- [Write the langfuse compose spec](#write-the-langfuse-compose-spec)
|
||||
- [A Note on Volumes](#a-note-on-volumes)
|
||||
- [Convert langfuse compose spec to quadlets](#convert-langfuse-compose-spec-to-quadlets)
|
||||
- [Create any container-mounted directories](#create-any-container-mounted-directories)
|
||||
- [Start and enable your systemd quadlet](#start-and-enable-your-systemd-quadlet)
|
||||
- [Expose langfuse](#expose-langfuse)
|
||||
- [firewalld](#firewalld)
|
||||
- [Backup langfuse](#backup-langfuse)
|
||||
- [Upgrade langfuse](#upgrade-langfuse)
|
||||
- [Upgrade Quadlets](#upgrade-quadlets)
|
||||
- [Uninstall](#uninstall)
|
||||
- [Notes](#notes)
|
||||
- [SELinux](#selinux)
|
||||
|
||||
## Setup langfuse Project
|
||||
|
||||
- [ ] Copy and rename this folder to active/container_langfuse
|
||||
- [ ] Find and replace langfuse with the name of the service.
|
||||
- [ ] Create the rootless user to run the podman containers
|
||||
- [ ] Write the compose.yaml spec for your service
|
||||
- [ ] Convert the compose.yaml spec to a quadlet
|
||||
- [ ] Install the quadlet on the podman server
|
||||
- [ ] Expose the quadlet service
|
||||
- [ ] Install a backup service and timer
|
||||
|
||||
## Install langfuse
|
||||
|
||||
### Create the langfuse user
|
||||
|
||||
```bash
|
||||
# SSH into your podman server as root
|
||||
useradd langfuse
|
||||
loginctl enable-linger $(id -u langfuse)
|
||||
systemctl --user --machine=langfuse@.host enable podman-restart
|
||||
systemctl --user --machine=langfuse@.host enable --now podman.socket
|
||||
mkdir -p /home/langfuse/.config/containers/systemd
|
||||
```
|
||||
|
||||
### Write the langfuse compose spec
|
||||
|
||||
Edit the compose.yaml at active/container_langfuse/compose/compose.yaml
|
||||
|
||||
#### A Note on Volumes
|
||||
|
||||
Named volumes are stored at `/home/langfuse/.local/share/containers/storage/volumes/`.
|
||||
|
||||
### Convert langfuse compose spec to quadlets
|
||||
|
||||
Run the following to convert a compose.yaml into the various `.container` files for systemd:
|
||||
|
||||
```bash
|
||||
# Generate the systemd service
|
||||
podman run \
|
||||
--security-opt label=disable \
|
||||
--rm \
|
||||
-v $(pwd)/active/container_langfuse/compose:/compose \
|
||||
-v $(pwd)/active/container_langfuse/quadlets:/quadlets \
|
||||
quay.io/k9withabone/podlet \
|
||||
-f /quadlets \
|
||||
-i \
|
||||
--overwrite \
|
||||
compose /compose/compose.yaml
|
||||
|
||||
# Copy the files to the server
|
||||
export PODMAN_SERVER=
|
||||
scp -r active/container_langfuse/quadlets/. $PODMAN_SERVER:/home/langfuse/.config/containers/systemd/
|
||||
ssh $PODMAN_SERVER chown -R langfuse:langfuse /home/langfuse/.config/containers/systemd/
|
||||
```
|
||||
|
||||
### Create any container-mounted directories
|
||||
|
||||
SSH into your podman server as root:
|
||||
|
||||
```bash
|
||||
machinectl shell langfuse@
|
||||
podman unshare
|
||||
mkdir some_volume
|
||||
# Chown to the namespaced user with UID 1000
|
||||
# This will be some really obscure UID outside the namespace
|
||||
# This will also solve most permission denied errors
|
||||
chown -R 1000:1000 some_volume
|
||||
```
|
||||
|
||||
### Start and enable your systemd quadlet
|
||||
|
||||
SSH into your podman server as root:
|
||||
|
||||
```bash
|
||||
machinectl shell langfuse@
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user restart langfuse
|
||||
# Enable auto-update service which will pull new container images automatically every day
|
||||
systemctl --user enable --now podman-auto-update.timer
|
||||
```
|
||||
|
||||
### Expose langfuse
|
||||
|
||||
1. If you need a domain, follow the [DDNS instructions](/active/container_ddns/ddns.md#install-a-new-ddns-service)
|
||||
2. For a web service, follow the [Caddy instructions](/active/container_caddy/caddy.md#adding-a-new-caddy-record)
|
||||
3. Finally, follow your OS's guide for opening ports via its firewall service.
|
||||
|
||||
#### firewalld
|
||||
|
||||
```bash
|
||||
# command to get current active zone and default zone
|
||||
firewall-cmd --get-active-zones
|
||||
firewall-cmd --get-default-zone
|
||||
|
||||
# command to open 443 on tcp
|
||||
firewall-cmd --permanent --zone=<zone> --add-port=443/tcp
|
||||
|
||||
# command to open 80 and 443 on tcp and udp
|
||||
firewall-cmd --permanent --zone=<zone> --add-port={80,443}/{tcp,udp}
|
||||
|
||||
# command to list available services and then open http and https
|
||||
firewall-cmd --get-services
|
||||
firewall-cmd --permanent --zone=<zone> --add-service={http,https}
|
||||
```
|
||||
|
||||
## Backup langfuse
|
||||
|
||||
Follow the [Borg Backup instructions](/active/systemd_borg/borg.md#set-up-a-client-for-backup)
|
||||
|
||||
## Upgrade langfuse
|
||||
|
||||
### Upgrade Quadlets
|
||||
|
||||
Upgrades should be a repeat of [writing the compose spec](#convert-langfuse-compose-spec-to-quadlets) and [installing the quadlets](#start-and-enable-your-systemd-quadlet)
|
||||
|
||||
```bash
|
||||
export PODMAN_SERVER=
|
||||
scp -r quadlets/. $PODMAN_SERVER$:/home/langfuse/.config/containers/systemd/
|
||||
ssh langfuse systemctl --user daemon-reload
|
||||
ssh langfuse systemctl --user restart langfuse
|
||||
```
|
||||
|
||||
## Uninstall
|
||||
|
||||
```bash
|
||||
# Stop the user's services
|
||||
systemctl --user disable podman-restart
|
||||
podman container stop --all
|
||||
systemctl --user disable --now podman.socket
|
||||
systemctl --user disable --now podman-auto-update.timer
|
||||
|
||||
# Delete the user (this won't delete their home directory)
|
||||
# userdel might spit out an error like:
|
||||
# userdel: user langfuse is currently used by process 591255
|
||||
# kill those processes and try again
|
||||
userdel langfuse
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
### SELinux
|
||||
|
||||
<https://blog.christophersmart.com/2021/01/31/podman-volumes-and-selinux/>
|
||||
|
||||
:z allows a container to share a mounted volume with all other containers.
|
||||
|
||||
:Z allows a container to reserve a mounted volume and prevents any other container from accessing.
|
||||
@@ -0,0 +1,21 @@
|
||||
[Container]
|
||||
Pod=langfuse.pod
|
||||
ContainerName=langfuse-clickhouse
|
||||
Environment=CLICKHOUSE_DB=default
|
||||
Environment=CLICKHOUSE_USER=clickhouse
|
||||
Environment=CLICKHOUSE_PASSWORD=clickhouse
|
||||
User=101:101
|
||||
Image=docker.io/clickhouse/clickhouse-server
|
||||
Volume=langfuse-clickhouse_data.volume:/var/lib/clickhouse
|
||||
Volume=langfuse-clickhouse_logs.volume:/var/log/clickhouse-server
|
||||
HealthCmd=wget --no-verbose --tries=1 --spider http://localhost:8123/ping
|
||||
HealthInterval=5s
|
||||
HealthTimeout=5s
|
||||
HealthRetries=10
|
||||
HealthStartPeriod=1s
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -0,0 +1,5 @@
|
||||
[Volume]
|
||||
VolumeName=langfuse_clickhouse_data
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -0,0 +1,5 @@
|
||||
[Volume]
|
||||
VolumeName=langfuse_clickhouse_logs
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -0,0 +1,20 @@
|
||||
[Container]
|
||||
Pod=langfuse.pod
|
||||
ContainerName=langfuse-minio
|
||||
Environment=MINIO_ROOT_USER=minio
|
||||
Environment=MINIO_ROOT_PASSWORD=miniosecret
|
||||
Image=cgr.dev/chainguard/minio
|
||||
Volume=langfuse-minio_data.volume:/data
|
||||
Entrypoint=sh
|
||||
Exec=-c 'mkdir -p /data/langfuse && minio server --address ":9000" --console-address ":9001" /data'
|
||||
HealthCmd=mc ready local
|
||||
HealthInterval=1s
|
||||
HealthTimeout=5s
|
||||
HealthRetries=5
|
||||
HealthStartPeriod=1s
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -0,0 +1,5 @@
|
||||
[Volume]
|
||||
VolumeName=langfuse_minio_data
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -0,0 +1,20 @@
|
||||
[Container]
|
||||
Pod=langfuse.pod
|
||||
ContainerName=langfuse-postgres
|
||||
Environment=POSTGRES_USER=postgres
|
||||
Environment=POSTGRES_PASSWORD=postgres
|
||||
Environment=POSTGRES_DB=postgres
|
||||
Environment=TZ=UTC
|
||||
Environment=PGTZ=UTC
|
||||
Image=docker.io/postgres:17
|
||||
Volume=langfuse-postgres_data.volume:/var/lib/postgresql/data
|
||||
HealthCmd=pg_isready -U postgres
|
||||
HealthInterval=3s
|
||||
HealthTimeout=3s
|
||||
HealthRetries=10
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -0,0 +1,5 @@
|
||||
[Volume]
|
||||
VolumeName=langfuse_postgres_data
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -0,0 +1,17 @@
|
||||
[Container]
|
||||
Pod=langfuse.pod
|
||||
ContainerName=langfuse-redis
|
||||
Environment=REDIS_AUTH=myredissecret
|
||||
Image=docker.io/redis:7
|
||||
Volume=langfuse-redis_data.volume:/data
|
||||
Exec=--requirepass myredissecret --maxmemory-policy noeviction
|
||||
HealthCmd=redis-cli ping
|
||||
HealthInterval=3s
|
||||
HealthTimeout=10s
|
||||
HealthRetries=10
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -0,0 +1,5 @@
|
||||
[Volume]
|
||||
VolumeName=langfuse_redis_data
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -0,0 +1,71 @@
|
||||
[Unit]
|
||||
Requires=langfuse-postgres.service langfuse-clickhouse.service langfuse-redis.service langfuse-minio.service
|
||||
|
||||
[Container]
|
||||
Pod=langfuse.pod
|
||||
ContainerName=langfuse-web
|
||||
Environment=NEXTAUTH_URL=http://localhost:3000
|
||||
Environment=DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres
|
||||
Environment=SALT=mysalt
|
||||
Environment=ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000
|
||||
Environment=TELEMETRY_ENABLED=true
|
||||
Environment=LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES=false
|
||||
Environment=CLICKHOUSE_MIGRATION_URL=clickhouse://localhost:9000
|
||||
Environment=CLICKHOUSE_URL=http://localhost:8123
|
||||
Environment=CLICKHOUSE_USER=clickhouse
|
||||
Environment=CLICKHOUSE_PASSWORD=clickhouse
|
||||
Environment=CLICKHOUSE_CLUSTER_ENABLED=false
|
||||
Environment=LANGFUSE_USE_AZURE_BLOB=false
|
||||
Environment=LANGFUSE_USE_OCI_NATIVE_OBJECT_STORAGE=false
|
||||
Environment=LANGFUSE_OCI_AUTH_TYPE=workload_identity
|
||||
Environment=LANGFUSE_S3_EVENT_UPLOAD_BUCKET=langfuse
|
||||
Environment=LANGFUSE_S3_EVENT_UPLOAD_REGION=auto
|
||||
Environment=LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID=minio
|
||||
Environment=LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY=miniosecret
|
||||
Environment=LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT=http://localhost:9000
|
||||
Environment=LANGFUSE_S3_EVENT_UPLOAD_FORCE_PATH_STYLE=true
|
||||
Environment=LANGFUSE_S3_EVENT_UPLOAD_PREFIX=events/
|
||||
Environment=LANGFUSE_S3_MEDIA_UPLOAD_BUCKET=langfuse
|
||||
Environment=LANGFUSE_S3_MEDIA_UPLOAD_REGION=auto
|
||||
Environment=LANGFUSE_S3_MEDIA_UPLOAD_ACCESS_KEY_ID=minio
|
||||
Environment=LANGFUSE_S3_MEDIA_UPLOAD_SECRET_ACCESS_KEY=miniosecret
|
||||
Environment=LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT=http://localhost:9090
|
||||
Environment=LANGFUSE_S3_MEDIA_UPLOAD_FORCE_PATH_STYLE=true
|
||||
Environment=LANGFUSE_S3_MEDIA_UPLOAD_PREFIX=media/
|
||||
Environment=LANGFUSE_S3_BATCH_EXPORT_ENABLED=false
|
||||
Environment=LANGFUSE_S3_BATCH_EXPORT_BUCKET=langfuse
|
||||
Environment=LANGFUSE_S3_BATCH_EXPORT_PREFIX=exports/
|
||||
Environment=LANGFUSE_S3_BATCH_EXPORT_REGION=auto
|
||||
Environment=LANGFUSE_S3_BATCH_EXPORT_ENDPOINT=http://localhost:9000
|
||||
Environment=LANGFUSE_S3_BATCH_EXPORT_EXTERNAL_ENDPOINT=http://localhost:9090
|
||||
Environment=LANGFUSE_S3_BATCH_EXPORT_ACCESS_KEY_ID=minio
|
||||
Environment=LANGFUSE_S3_BATCH_EXPORT_SECRET_ACCESS_KEY=miniosecret
|
||||
Environment=LANGFUSE_S3_BATCH_EXPORT_FORCE_PATH_STYLE=true
|
||||
Environment=LANGFUSE_INGESTION_QUEUE_DELAY_MS=
|
||||
Environment=LANGFUSE_INGESTION_CLICKHOUSE_WRITE_INTERVAL_MS=
|
||||
Environment=REDIS_HOST=localhost
|
||||
Environment=REDIS_PORT=6379
|
||||
Environment=REDIS_AUTH=myredissecret
|
||||
Environment=REDIS_TLS_ENABLED=false
|
||||
Environment=REDIS_TLS_CA=/certs/ca.crt
|
||||
Environment=REDIS_TLS_CERT=/certs/redis.crt
|
||||
Environment=REDIS_TLS_KEY=/certs/redis.key
|
||||
Environment=EMAIL_FROM_ADDRESS=
|
||||
Environment=SMTP_CONNECTION_URL=
|
||||
Environment=NEXTAUTH_SECRET=mysecret
|
||||
Environment=LANGFUSE_INIT_ORG_ID=
|
||||
Environment=LANGFUSE_INIT_ORG_NAME=
|
||||
Environment=LANGFUSE_INIT_PROJECT_ID=
|
||||
Environment=LANGFUSE_INIT_PROJECT_NAME=
|
||||
Environment=LANGFUSE_INIT_PROJECT_PUBLIC_KEY=
|
||||
Environment=LANGFUSE_INIT_PROJECT_SECRET_KEY=
|
||||
Environment=LANGFUSE_INIT_USER_EMAIL=admin@ducoterra.net
|
||||
Environment=LANGFUSE_INIT_USER_NAME=admin
|
||||
Environment=LANGFUSE_INIT_USER_PASSWORD=testpass
|
||||
Image=docker.io/langfuse/langfuse:3
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -0,0 +1,61 @@
|
||||
[Unit]
|
||||
Requires=langfuse-postgres.service langfuse-clickhouse.service langfuse-redis.service langfuse-minio.service
|
||||
|
||||
[Container]
|
||||
Pod=langfuse.pod
|
||||
ContainerName=langfuse-worker
|
||||
Environment=NEXTAUTH_URL=http://localhost:3000
|
||||
Environment=DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres
|
||||
Environment=SALT=mysalt
|
||||
Environment=ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000
|
||||
Environment=TELEMETRY_ENABLED=true
|
||||
Environment=LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES=false
|
||||
Environment=CLICKHOUSE_MIGRATION_URL=clickhouse://localhost:9000
|
||||
Environment=CLICKHOUSE_URL=http://localhost:8123
|
||||
Environment=CLICKHOUSE_USER=clickhouse
|
||||
Environment=CLICKHOUSE_PASSWORD=clickhouse
|
||||
Environment=CLICKHOUSE_CLUSTER_ENABLED=false
|
||||
Environment=LANGFUSE_USE_AZURE_BLOB=false
|
||||
Environment=LANGFUSE_USE_OCI_NATIVE_OBJECT_STORAGE=false
|
||||
Environment=LANGFUSE_OCI_AUTH_TYPE=workload_identity
|
||||
Environment=LANGFUSE_S3_EVENT_UPLOAD_BUCKET=langfuse
|
||||
Environment=LANGFUSE_S3_EVENT_UPLOAD_REGION=auto
|
||||
Environment=LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID=minio
|
||||
Environment=LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY=miniosecret
|
||||
Environment=LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT=http://localhost:9000
|
||||
Environment=LANGFUSE_S3_EVENT_UPLOAD_FORCE_PATH_STYLE=true
|
||||
Environment=LANGFUSE_S3_EVENT_UPLOAD_PREFIX=events/
|
||||
Environment=LANGFUSE_S3_MEDIA_UPLOAD_BUCKET=langfuse
|
||||
Environment=LANGFUSE_S3_MEDIA_UPLOAD_REGION=auto
|
||||
Environment=LANGFUSE_S3_MEDIA_UPLOAD_ACCESS_KEY_ID=minio
|
||||
Environment=LANGFUSE_S3_MEDIA_UPLOAD_SECRET_ACCESS_KEY=miniosecret
|
||||
Environment=LANGFUSE_S3_MEDIA_UPLOAD_ENDPOINT=http://localhost:9090
|
||||
Environment=LANGFUSE_S3_MEDIA_UPLOAD_FORCE_PATH_STYLE=true
|
||||
Environment=LANGFUSE_S3_MEDIA_UPLOAD_PREFIX=media/
|
||||
Environment=LANGFUSE_S3_BATCH_EXPORT_ENABLED=false
|
||||
Environment=LANGFUSE_S3_BATCH_EXPORT_BUCKET=langfuse
|
||||
Environment=LANGFUSE_S3_BATCH_EXPORT_PREFIX=exports/
|
||||
Environment=LANGFUSE_S3_BATCH_EXPORT_REGION=auto
|
||||
Environment=LANGFUSE_S3_BATCH_EXPORT_ENDPOINT=http://localhost:9000
|
||||
Environment=LANGFUSE_S3_BATCH_EXPORT_EXTERNAL_ENDPOINT=http://localhost:9090
|
||||
Environment=LANGFUSE_S3_BATCH_EXPORT_ACCESS_KEY_ID=minio
|
||||
Environment=LANGFUSE_S3_BATCH_EXPORT_SECRET_ACCESS_KEY=miniosecret
|
||||
Environment=LANGFUSE_S3_BATCH_EXPORT_FORCE_PATH_STYLE=true
|
||||
Environment=LANGFUSE_INGESTION_QUEUE_DELAY_MS=
|
||||
Environment=LANGFUSE_INGESTION_CLICKHOUSE_WRITE_INTERVAL_MS=
|
||||
Environment=REDIS_HOST=localhost
|
||||
Environment=REDIS_PORT=6379
|
||||
Environment=REDIS_AUTH=myredissecret
|
||||
Environment=REDIS_TLS_ENABLED=false
|
||||
Environment=REDIS_TLS_CA=/certs/ca.crt
|
||||
Environment=REDIS_TLS_CERT=/certs/redis.crt
|
||||
Environment=REDIS_TLS_KEY=/certs/redis.key
|
||||
Environment=EMAIL_FROM_ADDRESS=
|
||||
Environment=SMTP_CONNECTION_URL=
|
||||
Image=docker.io/langfuse/langfuse-worker:3
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -0,0 +1,12 @@
|
||||
[Pod]
|
||||
PublishPort=3000:3000/tcp
|
||||
PublishPort=127.0.0.1:3030:3030/tcp
|
||||
PublishPort=127.0.0.1:8123:8123/tcp
|
||||
PublishPort=127.0.0.1:9000:9000/tcp
|
||||
PublishPort=9090:9000/tcp
|
||||
PublishPort=127.0.0.1:9091:9001/tcp
|
||||
PublishPort=127.0.0.1:6379:6379/tcp
|
||||
PublishPort=127.0.0.1:5432:5432/tcp
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -11,7 +11,7 @@ HealthInterval=30s
|
||||
HealthRetries=3
|
||||
HealthStartPeriod=40s
|
||||
HealthTimeout=10s
|
||||
Image=ghcr.io/berriai/litellm-database:v1.83.14-stable.patch.3
|
||||
Image=ghcr.io/berriai/litellm-database:main-stable
|
||||
Volume=/home/ai/litellm-config.yaml:/app/config.yaml:z
|
||||
Exec=--config=/app/config.yaml
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# NTFY
|
||||
|
||||
## Running ntfy
|
||||
|
||||
```bash
|
||||
podman run \
|
||||
-v ntfy:/var/cache/ntfy \
|
||||
-v ./active/container_ntfy/server.yml:/etc/ntfy/server.yml:z \
|
||||
-p 8001:80 \
|
||||
-it \
|
||||
docker.io/binwiederhier/ntfy:latest \
|
||||
serve \
|
||||
--cache-file /var/cache/ntfy/cache.db
|
||||
```
|
||||
|
||||
## Testing with Curl
|
||||
|
||||
```bash
|
||||
# Publish a topic
|
||||
curl -d "Backup successful" localhost:8001/mytopic
|
||||
|
||||
# Listen on a topic
|
||||
curl -s localhost:8001/mytopic
|
||||
```
|
||||
@@ -0,0 +1 @@
|
||||
# base-url:
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"type": "micro",
|
||||
"wake_word": "Alexa",
|
||||
"author": "Kevin Ahrendt",
|
||||
"website": "https://www.kevinahrendt.com/",
|
||||
"model": "alexa.tflite",
|
||||
"trained_languages": ["en"],
|
||||
"version": 2,
|
||||
"micro": {
|
||||
"probability_cutoff": 0.9,
|
||||
"sliding_window_size": 5,
|
||||
"feature_step_size": 10,
|
||||
"tensor_arena_size": 22348,
|
||||
"minimum_esphome_version": "2024.7.0"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -1,9 +1,12 @@
|
||||
esphome:
|
||||
name: tab2
|
||||
friendly_name: M5Stack Tab5 2
|
||||
name: dickhead
|
||||
friendly_name: Dickhead
|
||||
on_boot:
|
||||
# Set the charing icon to the correct state on boot
|
||||
# Set the charging icon to the correct state on boot
|
||||
- then:
|
||||
- logger.log: "Delaying backlight initialization"
|
||||
- delay: 2s
|
||||
- logger.log: "End delay"
|
||||
- if:
|
||||
condition:
|
||||
lambda: return id(charging).state;
|
||||
@@ -17,6 +20,7 @@ esphome:
|
||||
esp32:
|
||||
board: esp32-p4-evboard
|
||||
flash_size: 16MB
|
||||
cpu_frequency: 360MHz
|
||||
framework:
|
||||
type: esp-idf
|
||||
advanced:
|
||||
@@ -36,6 +40,7 @@ esp32_hosted:
|
||||
|
||||
logger:
|
||||
hardware_uart: USB_SERIAL_JTAG
|
||||
level: DEBUG
|
||||
|
||||
psram:
|
||||
mode: hex
|
||||
@@ -53,8 +58,8 @@ ota:
|
||||
platform: esphome
|
||||
|
||||
wifi:
|
||||
ssid: !secret wifi_ssid
|
||||
password: !secret wifi_password
|
||||
ssid: !secret mobile_wifi_ssid
|
||||
password: !secret mobile_wifi_pass
|
||||
fast_connect: true
|
||||
on_connect:
|
||||
- lvgl.label.update:
|
||||
@@ -63,15 +68,50 @@ wifi:
|
||||
- select.set:
|
||||
id: dac_output
|
||||
option: "LINE1"
|
||||
- lvgl.label.update:
|
||||
id: lbl_ip
|
||||
text: !lambda return id(ip_addr).state;
|
||||
- lvgl.label.update:
|
||||
id: lbl_ap
|
||||
text: !lambda return id(ssid).state;
|
||||
on_disconnect:
|
||||
- lvgl.label.update:
|
||||
id: lbl_status
|
||||
text: "DISCONNECTED"
|
||||
# ap:
|
||||
# password: !secret hotspot_password
|
||||
# ap_timeout: 90s
|
||||
ap:
|
||||
password: !secret hotspot_password
|
||||
ap_timeout: 30s
|
||||
|
||||
# captive_portal:
|
||||
captive_portal:
|
||||
|
||||
text_sensor:
|
||||
- platform: wifi_info
|
||||
ip_address:
|
||||
id: ip_addr
|
||||
name: Device IP Address
|
||||
address_0:
|
||||
name: Device IP Address 0
|
||||
address_1:
|
||||
name: Device IP Address 1
|
||||
address_2:
|
||||
name: Device IP Address 2
|
||||
address_3:
|
||||
name: Device IP Address 3
|
||||
address_4:
|
||||
name: Device IP Address 4
|
||||
ssid:
|
||||
id: ssid
|
||||
name: Device Connected SSID
|
||||
bssid:
|
||||
name: Device Connected BSSID
|
||||
mac_address:
|
||||
name: Device Mac Wifi Address
|
||||
scan_results:
|
||||
name: Device Latest Scan Results
|
||||
dns_address:
|
||||
name: Device DNS Address
|
||||
power_save_mode:
|
||||
name: Device Wifi Power Save Mode
|
||||
|
||||
time:
|
||||
- platform: sntp
|
||||
@@ -82,14 +122,13 @@ time:
|
||||
- 1.pool.ntp.org
|
||||
- 2.pool.ntp.org
|
||||
|
||||
# wireguard:
|
||||
# address: !secret tab1_wg_ip
|
||||
# private_key: !secret tab1_wg_pk
|
||||
# peer_endpoint: !secret wg_host
|
||||
# peer_public_key: !secret wg_pubkey
|
||||
|
||||
# # Optional keepalive (disabled by default)
|
||||
# peer_persistent_keepalive: 25s
|
||||
wireguard:
|
||||
address: !secret tab1_wg_ip
|
||||
private_key: !secret tab1_wg_pk
|
||||
peer_endpoint: !secret wg_host
|
||||
peer_public_key: !secret wg_pubkey
|
||||
netmask: 0.0.0.0
|
||||
peer_persistent_keepalive: 25s
|
||||
|
||||
i2c:
|
||||
- id: bsp_bus
|
||||
@@ -196,6 +235,39 @@ binary_sensor:
|
||||
pin:
|
||||
pi4ioe5v6408: pi4ioe1
|
||||
number: 7
|
||||
- platform: lvgl
|
||||
widget: volume_up_widget
|
||||
name: Volume Up Button
|
||||
on_press:
|
||||
then:
|
||||
- logger.log: "Button pressed"
|
||||
- media_player.volume_up:
|
||||
id: tab5_media_player
|
||||
- delay: 100ms
|
||||
- lvgl.label.update:
|
||||
id: lbl_volume
|
||||
text: !lambda return to_string(int(id(tab5_media_player).volume * 100));
|
||||
- light.turn_on:
|
||||
id: backlight
|
||||
brightness: !lambda |-
|
||||
float current_value = id(backlight).current_values.get_brightness();
|
||||
return current_value < 0.6 ? 0.6 : current_value + 0.2;
|
||||
- platform: lvgl
|
||||
widget: volume_down_widget
|
||||
name: Volume Down Button
|
||||
on_press:
|
||||
then:
|
||||
- logger.log: "Button pressed"
|
||||
- media_player.volume_down:
|
||||
id: tab5_media_player
|
||||
- lvgl.label.update:
|
||||
id: lbl_volume
|
||||
text: !lambda return to_string(int(id(tab5_media_player).volume * 100));
|
||||
- light.turn_on:
|
||||
id: backlight
|
||||
brightness: !lambda |-
|
||||
float current_value = id(backlight).current_values.get_brightness();
|
||||
return current_value < 0.6 ? 0.6 : current_value - 0.2;
|
||||
|
||||
sensor:
|
||||
- platform: ina226
|
||||
@@ -255,8 +327,8 @@ touchscreen:
|
||||
- logger.log: "LVGL resuming"
|
||||
- lvgl.resume:
|
||||
- light.turn_on: backlight
|
||||
on_release:
|
||||
- media_player.stop:
|
||||
# on_release:
|
||||
# - media_player.stop:
|
||||
|
||||
esp_ldo:
|
||||
- voltage: 2.5V
|
||||
@@ -287,7 +359,7 @@ light:
|
||||
restore_mode: ALWAYS_ON
|
||||
default_transition_length: 250ms
|
||||
initial_state:
|
||||
brightness: "100%"
|
||||
brightness: "50%"
|
||||
|
||||
image:
|
||||
defaults:
|
||||
@@ -318,20 +390,41 @@ lvgl:
|
||||
transition_length: 15s
|
||||
- lvgl.pause:
|
||||
widgets:
|
||||
- obj:
|
||||
align: TOP_MID
|
||||
width: 100%
|
||||
height: 100%
|
||||
layout:
|
||||
type: flex
|
||||
flex_flow: column
|
||||
flex_align_main: START
|
||||
flex_align_track: center
|
||||
flex_align_cross: center
|
||||
widgets:
|
||||
- label:
|
||||
align: TOP_MID
|
||||
id: lbl_status
|
||||
text_font: montserrat_48
|
||||
text: "CONNECTING..."
|
||||
- label:
|
||||
align: TOP_MID
|
||||
id: lbl_ap
|
||||
text_font: montserrat_22
|
||||
text: "CONNECTING..."
|
||||
- label:
|
||||
align: TOP_MID
|
||||
id: lbl_ip
|
||||
text_font: montserrat_22
|
||||
text: "CONNECTING..."
|
||||
- image:
|
||||
id: listen_icon_widget
|
||||
src: va_idle
|
||||
align: CENTER
|
||||
- label:
|
||||
align: TOP_MID
|
||||
id: lbl_status
|
||||
text_font: montserrat_48
|
||||
text: "CONNECTING..."
|
||||
- label:
|
||||
align: BOTTOM_LEFT
|
||||
id: lbl_version
|
||||
text_font: montserrat_12
|
||||
text: "v0.5"
|
||||
text: "v0.6"
|
||||
- label:
|
||||
align: BOTTOM_RIGHT
|
||||
id: lbl_battery
|
||||
@@ -341,21 +434,38 @@ lvgl:
|
||||
id: charging_icon_widget
|
||||
src: charging_icon
|
||||
align: TOP_RIGHT
|
||||
- slider:
|
||||
id: backlight_slider
|
||||
- button:
|
||||
id: volume_up_widget
|
||||
widgets:
|
||||
- label:
|
||||
text: "\uF028"
|
||||
text_font: montserrat_48
|
||||
text_align: CENTER
|
||||
align: CENTER
|
||||
x: 20
|
||||
y: 50
|
||||
width: 30
|
||||
height: 220
|
||||
y: 20
|
||||
width: 100
|
||||
height: 100
|
||||
pad_all: 8
|
||||
min_value: 0
|
||||
max_value: 255
|
||||
on_release:
|
||||
- homeassistant.action:
|
||||
action: light.turn_on
|
||||
data:
|
||||
entity_id: light.backlight
|
||||
brightness: !lambda return int(x);
|
||||
- button:
|
||||
id: volume_down_widget
|
||||
widgets:
|
||||
- label:
|
||||
text: "\uF027"
|
||||
text_font: montserrat_48
|
||||
text_align: CENTER
|
||||
align: CENTER
|
||||
x: 20
|
||||
y: 140
|
||||
width: 100
|
||||
height: 100
|
||||
pad_all: 8
|
||||
- label:
|
||||
x: 20
|
||||
y: 260
|
||||
id: lbl_volume
|
||||
text_font: montserrat_28
|
||||
text: !lambda return "%.1f",to_string(id(tab5_media_player).volume);
|
||||
|
||||
# The DAC Output select needs to be manually (or with an automation) changed to `LINE1` for the onboard speaker
|
||||
select:
|
||||
@@ -414,7 +524,7 @@ speaker:
|
||||
audio_dac: es8388_dac
|
||||
dac_type: external
|
||||
channel: mono
|
||||
buffer_duration: 100ms
|
||||
buffer_duration: 60000ms
|
||||
bits_per_sample: 16bit
|
||||
sample_rate: 48000
|
||||
|
||||
@@ -429,9 +539,12 @@ media_player:
|
||||
micro_wake_word:
|
||||
id: mww
|
||||
models:
|
||||
- okay_nabu
|
||||
- hey_mycroft
|
||||
- hey_jarvis
|
||||
- model:
|
||||
type: local
|
||||
path: alexa.json
|
||||
id: alexa
|
||||
probability_cutoff: 0.97
|
||||
sliding_window_size: 5
|
||||
on_wake_word_detected:
|
||||
- voice_assistant.start:
|
||||
wake_word: !lambda return wake_word;
|
||||
@@ -56,6 +56,13 @@ Make sure your permissions are set correctly
|
||||
sudo usermod -a -G dialout ducoterra
|
||||
```
|
||||
|
||||
or on Kinoite
|
||||
|
||||
```bash
|
||||
grep -E '^dialout:' /usr/lib/group | sudo tee -a /etc/group
|
||||
sudo usermod -aG dialout $USER
|
||||
```
|
||||
|
||||
Then "run" your config file
|
||||
|
||||
```bash
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"type": "micro",
|
||||
"wake_word": "hey dick head",
|
||||
"author": "peenfart",
|
||||
"website": "https://www.kevinahrendt.com/",
|
||||
"model": "./hey_dick_head.tflite",
|
||||
"trained_languages": ["en"],
|
||||
"version": 2,
|
||||
"micro": {
|
||||
"probability_cutoff": 0.97,
|
||||
"sliding_window_size": 5,
|
||||
"feature_step_size": 10,
|
||||
"tensor_arena_size": 22860,
|
||||
"minimum_esphome_version": "2024.7"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Submodule
+1
Submodule active/device_home_assistant/config added at 5a3ce1da52
@@ -0,0 +1,48 @@
|
||||
# Reese's Workstation
|
||||
|
||||
- [Reese's Workstation](#reeses-workstation)
|
||||
- [Virtual Machines](#virtual-machines)
|
||||
|
||||
## Virtual Machines
|
||||
|
||||
1. Blueprints are stored in active/software_osbuild
|
||||
2. Builds are stored in ${HOME}/Images/builds
|
||||
|
||||
```bash
|
||||
# These are saved in ~/.bashrc.d/osbuild.sh
|
||||
export OSBUILD_BLUEPRINT_DIR=active/software_osbuild
|
||||
export OSBUILD_CLOUD_DIR=${HOME}/Images/cloud
|
||||
export OSBUILD_BUILD_DIR=${HOME}/Images/builds
|
||||
export OSBUILD_IMAGE_DIR=${HOME}/Images/disks
|
||||
|
||||
# Push a blueprint
|
||||
composer-cli blueprints push ${OSBUILD_BLUEPRINT_DIR}/fedora43-base.toml
|
||||
|
||||
# Start a build
|
||||
composer-cli compose start fedora43-base qcow2
|
||||
|
||||
# Watch the build
|
||||
watch composer-cli compose list running
|
||||
|
||||
# Extract the build
|
||||
composer-cli compose image \
|
||||
--filename ${OSBUILD_BUILD_DIR}/fedora-43-base.qcow2 \
|
||||
92a2f1dd-9238-4d50-ab1d-cd7c8e355cac
|
||||
|
||||
# Convert the build to raw
|
||||
qemu-img convert -f qcow2 -O raw \
|
||||
${OSBUILD_BUILD_DIR}/fedora-43-base.qcow2 \
|
||||
${OSBUILD_IMAGE_DIR}/fedora-boot.raw
|
||||
|
||||
# Install (Change password for default user ducoterra!)
|
||||
virt-install \
|
||||
--boot uefi,firmware.feature0.name=secure-boot,firmware.feature0.enabled=no \
|
||||
--cpu host-passthrough --vcpus sockets=1,cores=8,threads=2 \
|
||||
--ram=8192 \
|
||||
--os-variant=fedora43 \
|
||||
--network default \
|
||||
--graphics none \
|
||||
--console pty,target.type=virtio \
|
||||
--name "fedora" \
|
||||
--import --disk "path=${OSBUILD_IMAGE_DIR}/fedora-boot.raw,bus=virtio"
|
||||
```
|
||||
@@ -0,0 +1,89 @@
|
||||
# KDE Notes
|
||||
|
||||
## Fixing alt tab or taskbar icons
|
||||
|
||||
1. System Settings - Window Management - Window Rules - Add New...
|
||||
2. Bring the buggy app window side-by-side with system settings.
|
||||
3. Click "Detect Window Properties..." in the bottom.
|
||||
4. Move the crosshair to the buggy window and click.
|
||||
5. Click "Window class (application)".
|
||||
6. Click "Add Property..." in the bottm left.
|
||||
7. Click "Desktop file name".
|
||||
8. Enter the base name of the desktop file
|
||||
|
||||
For flatpak, check `/var/lib/flatpak/exports/share/applications/`
|
||||
|
||||
For system apps, check `/usr/share/applications/`
|
||||
|
||||
For user apps, check `~/.local/share/applications/`
|
||||
|
||||
9. Click "Apply".
|
||||
10. The icon should be fixed immediately.
|
||||
|
||||
## KDE Connect
|
||||
|
||||
Allow KDE Connect via 1714-1764 tcp/udp
|
||||
|
||||
```bash
|
||||
# Set source address to allow connections
|
||||
sudo firewall-cmd \
|
||||
--zone=drop \
|
||||
--permanent \
|
||||
--add-port=1714-1764/udp \
|
||||
--add-port=1714-1764/tcp
|
||||
|
||||
sudo firewall-cmd --reload
|
||||
```
|
||||
|
||||
## Automatic Display Switching
|
||||
|
||||
```bash
|
||||
# List displays
|
||||
# Builtin: eDP-2
|
||||
# Roku: DP-13
|
||||
# Lenovo: DP-11
|
||||
kscreen-doctor -o
|
||||
|
||||
# Put the builtin display back to normal
|
||||
kscreen-doctor \
|
||||
output.eDP-2.enable \
|
||||
output.eDP-2.position.0,0 \
|
||||
output.eDP-2.primary \
|
||||
output.eDP-2.mode.2560x1600@165 \
|
||||
output.eDP-2.scale.1.25
|
||||
|
||||
# Show on Roku TV and Monitor
|
||||
kscreen-doctor \
|
||||
output.DP-11.enable \
|
||||
output.DP-11.position.0,0 \
|
||||
output.DP-11.mode.2560x1440@60 \
|
||||
output.DP-11.scale.1 \
|
||||
output.DP-13.enable \
|
||||
output.DP-13.position.0,0 \
|
||||
output.DP-13.mode.2560x1440@100 \
|
||||
output.DP-13.scale.1 \
|
||||
output.DP-13.primary \
|
||||
output.eDP-2.disable
|
||||
|
||||
# Show only on the roku TV
|
||||
kscreen-doctor \
|
||||
output.DP-11.enable \
|
||||
output.DP-11.position.0,0 \
|
||||
output.DP-11.primary \
|
||||
output.DP-11.mode.3840x2160@60 \
|
||||
output.DP-11.scale.2 \
|
||||
output.DP-13.disable \
|
||||
output.eDP-2.disable
|
||||
|
||||
# Mirror the builtin display to the roku tv
|
||||
kscreen-doctor \
|
||||
output.DP-11.enable \
|
||||
output.DP-11.position.0,0 \
|
||||
output.DP-11.primary \
|
||||
output.DP-11.mode.3840x2160@60 \
|
||||
output.DP-11.scale.2 \
|
||||
output.eDP-2.enable \
|
||||
output.eDP-2.mode.1920x1080@60 \
|
||||
output.eDP-2.scale.1 \
|
||||
output.eDP-2.position.0,0
|
||||
```
|
||||
+178
-251
@@ -1,114 +1,204 @@
|
||||
# Fedora Kinoite
|
||||
|
||||
- [Fedora Kinoite](#fedora-kinoite)
|
||||
- [TPM2 Luks Decryption](#tpm2-luks-decryption)
|
||||
- [Podman](#podman)
|
||||
- [Docker Compose and Docker Buildkit with Rootless Podman](#docker-compose-and-docker-buildkit-with-rootless-podman)
|
||||
- [rpm-ostree](#rpm-ostree)
|
||||
- [Git, Vim, etc](#git-vim-etc)
|
||||
- [Libvirt, Qemu, KVM](#libvirt-qemu-kvm)
|
||||
- [ROCM](#rocm)
|
||||
- [Ollama](#ollama)
|
||||
- [Network](#network)
|
||||
- [Hostname](#hostname)
|
||||
- [VLAN Setup with nmcli](#vlan-setup-with-nmcli)
|
||||
- [GPU Support in Distrobox](#gpu-support-in-distrobox)
|
||||
- [Automatic Display Switching](#automatic-display-switching)
|
||||
- [Adding users to groups (usermod replacement)](#adding-users-to-groups-usermod-replacement)
|
||||
- [System Prereqs](#system-prereqs)
|
||||
- [Toolbox](#toolbox)
|
||||
- [Clipboard Sharing](#clipboard-sharing)
|
||||
- [Running Host Commands](#running-host-commands)
|
||||
- [Saving a Toolbox](#saving-a-toolbox)
|
||||
- [Apps](#apps)
|
||||
- [Gear Lever](#gear-lever)
|
||||
- [VSCodium](#vscodium)
|
||||
- [Ansible](#ansible)
|
||||
- [OpenCode](#opencode)
|
||||
- [Nextcloud](#nextcloud)
|
||||
- [UV](#uv)
|
||||
- [NVM](#nvm)
|
||||
- [Set Hostname](#set-hostname)
|
||||
- [rpm-ostree Notes](#rpm-ostree-notes)
|
||||
- [Accent keys when holding a key](#accent-keys-when-holding-a-key)
|
||||
|
||||
## TPM2 Luks Decryption
|
||||
|
||||
Mostly taken from here:
|
||||
<https://gist.github.com/jdoss/777e8b52c8d88eb87467935769c98a95>
|
||||
|
||||
PCR reference for `--tpm2-pcrs` args
|
||||
|
||||
```text
|
||||
0: System firmware executable
|
||||
2: Kernel
|
||||
4: Bootloader
|
||||
7: Secure boot state
|
||||
8: Cmdline
|
||||
9: Initrd
|
||||
```
|
||||
|
||||
Basic commands:
|
||||
## Adding users to groups (usermod replacement)
|
||||
|
||||
```bash
|
||||
# Show tpm2 devices
|
||||
systemd-cryptenroll --tpm2-device=list
|
||||
# Show crypto luks block devices
|
||||
blkid -t TYPE=crypto_LUKS
|
||||
|
||||
# Enroll the tpm2 device with systemd-cryptenroll
|
||||
systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0,2,4,7,8,9 /dev/nvme0n1p3
|
||||
|
||||
# Reenroll
|
||||
systemd-cryptenroll /dev/nvme0n1p3 --wipe-slot=tpm2 --tpm2-device=auto --tpm2-pcrs=0,2,4,7,8,9
|
||||
|
||||
# Append to command line args
|
||||
rpm-ostree kargs --append=rd.luks.options=tpm2-device=auto
|
||||
grep -E '^weldr:' /usr/lib/group | sudo tee -a /etc/group
|
||||
sudo usermod -aG weldr $USER
|
||||
```
|
||||
|
||||
When you update you'll need to reenroll. Add this to your ~/.bashrc
|
||||
## System Prereqs
|
||||
|
||||
```bash
|
||||
# LUKS TPM2 commands
|
||||
alias tpm2-reenroll='sudo systemd-cryptenroll /dev/nvme0n1p3 --wipe-slot=tpm2 --tpm2-device=auto --tpm2-pcrs=0,2,4,7,8,9'
|
||||
# Appimage support
|
||||
rpm-ostree install fuse fuse-libs
|
||||
|
||||
# Editor
|
||||
rpm-ostree install vim
|
||||
|
||||
# Qemu/KVM
|
||||
rpm-ostree install virt-manager libvirt virt-install
|
||||
|
||||
# OSBuild
|
||||
rpm-ostree install -y osbuild-composer composer-cli openscap-scanner scap-security-guide
|
||||
|
||||
# Distrobox
|
||||
rpm-ostree install -y distrobox
|
||||
|
||||
# BTRFS Snapshots
|
||||
rpm-ostree install -y snapper
|
||||
|
||||
# Kitty Terminal
|
||||
# terminfo prevents the "WARNING: terminal is not fully functional" error
|
||||
rpm-ostree install -y kitty kitty-terminfo
|
||||
```
|
||||
|
||||
## Podman
|
||||
## Toolbox
|
||||
|
||||
Since you'll be using podman for most container-based services, you'll want to set the
|
||||
the podman auth file to somewhere persistent, otherwise it'll get deleted every time you
|
||||
reboot.
|
||||
### Clipboard Sharing
|
||||
|
||||
Add this to your `.bashrc`:
|
||||
Inside the toolbox run:
|
||||
|
||||
```bash
|
||||
# Podman auth file
|
||||
export REGISTRY_AUTH_FILE=$HOME/.podman-auth.json
|
||||
sudo dnf install -y wl-clipboard
|
||||
```
|
||||
|
||||
Source that and then run `podman login` to create the file.
|
||||
### Running Host Commands
|
||||
|
||||
### Docker Compose and Docker Buildkit with Rootless Podman
|
||||
You'll want to use toolbox for development. Unfortunately, toolbox doesn't
|
||||
integrate well with host system operations.
|
||||
|
||||
Allows you to use podman with full docker-compose compatibility.
|
||||
|
||||
<https://emersion.fr/blog/2025/using-podman-compose-and-buildkit/>
|
||||
I create a `toolbox.sh` in `~/.bashrc.d` with functions that map to host
|
||||
commands like so:
|
||||
|
||||
```bash
|
||||
rpm-ostree install docker-compose docker-buildx
|
||||
reboot
|
||||
systemctl --user enable --now podman.socket
|
||||
docker context create podman --docker host=unix://$XDG_RUNTIME_DIR/podman/podman.sock
|
||||
docker context use podman
|
||||
# Use flatpak-spawn --host podman inside toolbox, regular podman outside
|
||||
podman() {
|
||||
if [ -f /run/.containerenv ]; then
|
||||
flatpak-spawn --host podman "$@"
|
||||
else
|
||||
command podman "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
### Autostarting services with quadlets
|
||||
|
||||
If you want to run something as your user at boot (like a systemd process, think ollama) you can
|
||||
create a user quadlets like so:
|
||||
|
||||
```bash
|
||||
# Generate the .container file
|
||||
podman run --rm ghcr.io/containers/podlet --install --description "Local AI" \
|
||||
podman run \
|
||||
-d \
|
||||
-v ollama:/root/.ollama \
|
||||
-p 11434:11434 \
|
||||
--name ollama \
|
||||
--restart always \
|
||||
docker.io/ollama/ollama > ~/.config/containers/systemd/ollama.container
|
||||
|
||||
# Verify the service (Note the filename:service, this is required! You will get "Failed to prepare filename" without it)
|
||||
systemd-analyze verify ~/.config/containers/systemd/ollama.container:ollama.service
|
||||
|
||||
# Start the service
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user start ollama
|
||||
# Notify the user if they're trying to run a command that doesn't work in a toolbox
|
||||
rpm-ostree() {
|
||||
if [ -f /run/.containerenv ]; then
|
||||
echo "Command must be run outside toolbox."
|
||||
else
|
||||
command rpm-ostree "$@"
|
||||
fi
|
||||
}
|
||||
```
|
||||
|
||||
## rpm-ostree
|
||||
I have the following functions mapped:
|
||||
|
||||
1. podman
|
||||
2. rpm-ostree
|
||||
3. flatpak
|
||||
4. firewall-cmd
|
||||
|
||||
### Saving a Toolbox
|
||||
|
||||
After setting up a base toolbox you'll want to save it for future use. This is easy with podman commit.
|
||||
|
||||
```bash
|
||||
# 1. Commit the running container to a new image
|
||||
podman commit fedora-toolbox-44 my-toolbox-snapshot
|
||||
|
||||
# 2. Save the image to a tar file for later use
|
||||
podman save -o ~/my-toolbox-snapshot.tar my-toolbox-snapshot
|
||||
|
||||
# Load the image
|
||||
podman load -i ~/my-toolbox-snapshot.tar
|
||||
|
||||
# Create a new toolbox from it
|
||||
toolbox create --image my-toolbox-snapshot
|
||||
```
|
||||
|
||||
## Apps
|
||||
|
||||
Anything not mentioned here is installed via flatpak from flathub.
|
||||
|
||||
### Gear Lever
|
||||
|
||||
```bash
|
||||
flatpak install it.mijorus.gearlever
|
||||
```
|
||||
|
||||
### VSCodium
|
||||
|
||||
Install in toolbox.
|
||||
|
||||
<https://vscodium.com/#install-on-fedora-rhel-centos-rockylinux-opensuse-rpm-package>
|
||||
|
||||
```bash
|
||||
sudo tee -a /etc/yum.repos.d/vscodium.repo << 'EOF'
|
||||
[gitlab.com_paulcarroty_vscodium_repo]
|
||||
name=gitlab.com_paulcarroty_vscodium_repo
|
||||
baseurl=https://paulcarroty.gitlab.io/vscodium-deb-rpm-repo/rpms/
|
||||
enabled=1
|
||||
gpgcheck=1
|
||||
repo_gpgcheck=1
|
||||
gpgkey=https://gitlab.com/paulcarroty/vscodium-deb-rpm-repo/raw/master/pub.gpg
|
||||
metadata_expire=1h
|
||||
EOF
|
||||
|
||||
sudo dnf install codium
|
||||
```
|
||||
|
||||
### Ansible
|
||||
|
||||
```bash
|
||||
# Install pipx
|
||||
python3 -m ensurepip --user
|
||||
python3 -m pip install --user pipx
|
||||
pipx ensurepath
|
||||
|
||||
# Install ansible
|
||||
pipx install --include-deps ansible
|
||||
```
|
||||
|
||||
### OpenCode
|
||||
|
||||
```bash
|
||||
curl -fsSL https://opencode.ai/install | bash
|
||||
|
||||
# Install pyright LSP
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install --user pyright
|
||||
```
|
||||
|
||||
### Nextcloud
|
||||
|
||||
Download the AppImage. Install via Gear Lever.
|
||||
|
||||
<https://github.com/nextcloud-releases/desktop/releases>
|
||||
|
||||
| Source | Repo | Release file name |
|
||||
| ------ | -------------------------- | ----------------- |
|
||||
| Github | nextcloud-releases/desktop | *x86_64.AppImage |
|
||||
|
||||
### UV
|
||||
|
||||
```bash
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
```
|
||||
|
||||
### NVM
|
||||
|
||||
<https://www.nvmnode.com/guide/installation.html#nvm-install-for-linux-macos>
|
||||
|
||||
```bash
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
|
||||
```
|
||||
|
||||
## Set Hostname
|
||||
|
||||
```bash
|
||||
sudo hostnamectl hostname reesework16
|
||||
```
|
||||
|
||||
## rpm-ostree Notes
|
||||
|
||||
Note: if you don't need them, remove extra repos
|
||||
|
||||
@@ -133,169 +223,6 @@ rpm-ostree apply-live
|
||||
rpm-ostree status
|
||||
```
|
||||
|
||||
### Git, Vim, etc
|
||||
## Accent keys when holding a key
|
||||
|
||||
Some packages are nice to have at the system level.
|
||||
|
||||
```bash
|
||||
rpm-ostree install git vim
|
||||
```
|
||||
|
||||
### Libvirt, Qemu, KVM
|
||||
|
||||
```bash
|
||||
rpm-ostree install virt-manager libvirt
|
||||
systemctl enable --now libvirtd
|
||||
```
|
||||
|
||||
### ROCM
|
||||
|
||||
Credit to:
|
||||
<https://medium.com/@seancheo/running-generative-ai-on-amd-in-fedora-40-28aa3bebb187>
|
||||
|
||||
Here are most of the ROCM packages that you'll need.
|
||||
|
||||
Fedora is currently working on a ROCM meta package here:
|
||||
<https://packages.fedoraproject.org/pkgs/rocm/rocm/>
|
||||
|
||||
```bash
|
||||
rpm-ostree install \
|
||||
hipblas-devel \
|
||||
hipblaslt-devel \
|
||||
hipcc \
|
||||
hipcc-libomp-devel \
|
||||
hipcub-devel \
|
||||
hipfft-devel \
|
||||
hipfort-devel \
|
||||
hiprand-devel \
|
||||
hiprt-devel \
|
||||
hipsolver-devel \
|
||||
hipsparse-devel \
|
||||
rocalution-devel \
|
||||
rocblas-devel \
|
||||
rocfft-devel \
|
||||
rocm-clang-devel \
|
||||
rocm-clang-tools-extra-devel \
|
||||
rocm-cmake \
|
||||
rocm-comgr-devel \
|
||||
rocm-core-devel \
|
||||
rocm-hip-devel \
|
||||
rocm-libc++-devel \
|
||||
rocm-libc++-static \
|
||||
rocm-llvm-devel \
|
||||
rocm-omp-devel \
|
||||
rocm-runtime-devel \
|
||||
rocm-rpp-devel \
|
||||
rocm-smi-devel \
|
||||
rocminfo \
|
||||
rocdecode-devel \
|
||||
rocjpeg-devel \
|
||||
rocprim-devel \
|
||||
rocrand-devel \
|
||||
rocsolver-devel \
|
||||
rocsparse-devel \
|
||||
rocthrust-devel \
|
||||
roctracer-devel \
|
||||
miopen
|
||||
```
|
||||
|
||||
### Ollama
|
||||
|
||||
<https://ollama.com/download>
|
||||
|
||||
Run the installation script as normal. Make sure you have the [ROCM](#rocm) drivers installed
|
||||
for GPU acceleration. The script *should* automatically pull the ROCM drivers after installing
|
||||
the base packages. If not, you should install them manually.
|
||||
|
||||
For starting ollama as a service, follow the link below with one exception.
|
||||
|
||||
<https://github.com/ollama/ollama/blob/main/docs/linux.md#adding-ollama-as-a-startup-service-recommended>
|
||||
|
||||
```bash
|
||||
# Change the recommended home dir to /var/home/ollama
|
||||
sudo useradd -r -s /bin/false -U -m -d /var/home/ollama ollama
|
||||
```
|
||||
|
||||
## Network
|
||||
|
||||
### Hostname
|
||||
|
||||
```bash
|
||||
sudo hostnamectl hostname reesework16
|
||||
```
|
||||
|
||||
### VLAN Setup with nmcli
|
||||
|
||||
```bash
|
||||
# VLAN 2
|
||||
nmcli conn
|
||||
export NMCLI_DEVICE=enp195s0f4u1u3
|
||||
nmcli connection add type VLAN con-name $NMCLI_DEVICE.2 dev $NMCLI_DEVICE id 2
|
||||
```
|
||||
|
||||
## GPU Support in Distrobox
|
||||
|
||||
Fix for `error="failed to check permission on /dev/kfd: open /dev/kfd: invalid argument"`
|
||||
|
||||
```bash
|
||||
# You have to create the video and render group to /etc/group before you can use it
|
||||
sudo grep -E '^video:' /usr/lib/group | sudo tee -a /etc/group
|
||||
sudo grep -E '^render:' /usr/lib/group | sudo tee -a /etc/group
|
||||
sudo usermod -aG video $USER
|
||||
sudo usermod -aG render $USER
|
||||
```
|
||||
|
||||
Logout and log back in to adopt new groups.
|
||||
|
||||
## Automatic Display Switching
|
||||
|
||||
```bash
|
||||
# List displays
|
||||
# Builtin: eDP-2
|
||||
# Roku: DP-13
|
||||
# Lenovo: DP-11
|
||||
kscreen-doctor -o
|
||||
|
||||
# Put the builtin display back to normal
|
||||
kscreen-doctor \
|
||||
output.eDP-2.enable \
|
||||
output.eDP-2.position.0,0 \
|
||||
output.eDP-2.primary \
|
||||
output.eDP-2.mode.2560x1600@165 \
|
||||
output.eDP-2.scale.1.25
|
||||
|
||||
# Show on Roku TV and Monitor
|
||||
kscreen-doctor \
|
||||
output.DP-11.enable \
|
||||
output.DP-11.position.0,0 \
|
||||
output.DP-11.mode.2560x1440@60 \
|
||||
output.DP-11.scale.1 \
|
||||
output.DP-13.enable \
|
||||
output.DP-13.position.0,0 \
|
||||
output.DP-13.mode.2560x1440@100 \
|
||||
output.DP-13.scale.1 \
|
||||
output.DP-13.primary \
|
||||
output.eDP-2.disable
|
||||
|
||||
# Show only on the roku TV
|
||||
kscreen-doctor \
|
||||
output.DP-11.enable \
|
||||
output.DP-11.position.0,0 \
|
||||
output.DP-11.primary \
|
||||
output.DP-11.mode.3840x2160@60 \
|
||||
output.DP-11.scale.2 \
|
||||
output.DP-13.disable \
|
||||
output.eDP-2.disable
|
||||
|
||||
# Mirror the builtin display to the roku tv
|
||||
kscreen-doctor \
|
||||
output.DP-11.enable \
|
||||
output.DP-11.position.0,0 \
|
||||
output.DP-11.primary \
|
||||
output.DP-11.mode.3840x2160@60 \
|
||||
output.DP-11.scale.2 \
|
||||
output.eDP-2.enable \
|
||||
output.eDP-2.mode.1920x1080@60 \
|
||||
output.eDP-2.scale.1 \
|
||||
output.eDP-2.position.0,0
|
||||
```
|
||||
Settings -> keyboard -> virtual keyboard -> disable virtual keyboard
|
||||
@@ -4,6 +4,7 @@
|
||||
- [Installation](#installation)
|
||||
- [Setup SSH](#setup-ssh)
|
||||
- [DNF](#dnf)
|
||||
- [Version lock](#version-lock)
|
||||
- [Power Profiles with Tuned](#power-profiles-with-tuned)
|
||||
- [Fail2Ban](#fail2ban)
|
||||
- [BTRFS Parent Volumes](#btrfs-parent-volumes)
|
||||
@@ -84,6 +85,7 @@
|
||||
- [Sound Devices](#sound-devices)
|
||||
- [Install ffmpegthumbnailer, remove totem](#install-ffmpegthumbnailer-remove-totem)
|
||||
- [Add compatibility for HEIC to mogrify](#add-compatibility-for-heic-to-mogrify)
|
||||
- [Redirecting ports for rootless services](#redirecting-ports-for-rootless-services)
|
||||
|
||||
<https://docs.fedoraproject.org/en-US/fedora-server/installation/postinstallation-tasks/#_manage_system_updates>
|
||||
|
||||
@@ -127,6 +129,16 @@ dnf update --refresh -y
|
||||
dnf install -y git glances tmux vim python3-libdnf5 borgbackup tpm2-tools
|
||||
```
|
||||
|
||||
### Version lock
|
||||
|
||||
```bash
|
||||
sudo dnf install dnf-plugin-versionlock
|
||||
sudo dnf list <package> --showduplicates
|
||||
sudo dnf versionlock add <package_name>-<version>.x86_64
|
||||
sudo dnf versionlock list
|
||||
sudo dnf versionlock delete <package_name>
|
||||
```
|
||||
|
||||
## Power Profiles with Tuned
|
||||
|
||||
1. `dnf install tuned`
|
||||
@@ -1486,3 +1498,29 @@ sudo dnf install ffmpegthumbnailer
|
||||
```bash
|
||||
sudo dnf install libheic-freeworld
|
||||
```
|
||||
|
||||
## Redirecting ports for rootless services
|
||||
|
||||
```bash
|
||||
# ipv4
|
||||
sudo firewall-cmd \
|
||||
--permanent \
|
||||
--add-forward-port=port=80:proto=tcp:toport=8080
|
||||
|
||||
sudo firewall-cmd --add-rich-rule='forward-port port=80 protocol=tcp to-port=8080
|
||||
```
|
||||
|
||||
Or in ansible
|
||||
|
||||
```yaml
|
||||
- name: Forward ports
|
||||
ansible.posix.firewalld:
|
||||
port_forward:
|
||||
- port: "{{ item.from }}"
|
||||
proto: tcp
|
||||
toport: "{{ item.to }}"
|
||||
permanent: true
|
||||
state: enabled
|
||||
immediate: true
|
||||
loop: "{{ forward_ports }}"
|
||||
```
|
||||
@@ -1,2 +0,0 @@
|
||||
[Network]
|
||||
Internal=true
|
||||
@@ -1,14 +0,0 @@
|
||||
[Pod]
|
||||
Network=ai-internal.network
|
||||
# llama.cpp server
|
||||
PublishPort=8000:8000/tcp
|
||||
# llama.cpp embed
|
||||
PublishPort=8001:8001/tcp
|
||||
# llama.cpp instruct
|
||||
PublishPort=8002:8002/tcp
|
||||
# llama.cpp tool
|
||||
PublishPort=8003:8003/tcp
|
||||
# stable-diffusion.cpp gen
|
||||
PublishPort=1234:1234/tcp
|
||||
# stable-diffusion.cpp edit
|
||||
PublishPort=1235:1235/tcp
|
||||
@@ -3,10 +3,6 @@
|
||||
- [Self Hosted AI Stack](#self-hosted-ai-stack)
|
||||
- [Notes](#notes)
|
||||
- [Podman Volume Locations](#podman-volume-locations)
|
||||
- [List of Internal Links](#list-of-internal-links)
|
||||
- [Quick Install](#quick-install)
|
||||
- [Text Stack](#text-stack)
|
||||
- [Image Stack](#image-stack)
|
||||
- [Setup](#setup)
|
||||
- [Create the AI user](#create-the-ai-user)
|
||||
- [Helper aliases](#helper-aliases)
|
||||
@@ -36,16 +32,7 @@
|
||||
- [open-webui](#open-webui)
|
||||
- [Upgrade Open Webui](#upgrade-open-webui)
|
||||
- [lite-llm](#lite-llm)
|
||||
- [Install Services with Quadlets](#install-services-with-quadlets)
|
||||
- [API Keys](#api-keys)
|
||||
- [Internal and External Pods](#internal-and-external-pods)
|
||||
- [Llama CPP Server (Port 8000)](#llama-cpp-server-port-8000)
|
||||
- [Llama CPP Embedding Server (Port 8001)](#llama-cpp-embedding-server-port-8001)
|
||||
- [Llama CPP Instruct Server (Port 8002)](#llama-cpp-instruct-server-port-8002)
|
||||
- [Stable Diffusion CPP (Port 1234 and 1235)](#stable-diffusion-cpp-port-1234-and-1235)
|
||||
- [Open Webui (Port 8080)](#open-webui-port-8080)
|
||||
- [Install the update script](#install-the-update-script)
|
||||
- [Install Guest Open Webui with Start/Stop Services](#install-guest-open-webui-with-startstop-services)
|
||||
- [Install the update script](#install-the-update-script)
|
||||
- [Benchmark Results](#benchmark-results)
|
||||
- [Testing with Curl](#testing-with-curl)
|
||||
- [OpenAI API](#openai-api)
|
||||
@@ -77,33 +64,6 @@ hf-download ()
|
||||
|
||||
`~/.local/share/containers/storage/volumes/`
|
||||
|
||||
### List of Internal Links
|
||||
|
||||
- llama-cpp
|
||||
- llama-embed
|
||||
- llama-instruct
|
||||
- image-gen
|
||||
- image-edit
|
||||
- openwebui
|
||||
|
||||
## Quick Install
|
||||
|
||||
### Text Stack
|
||||
|
||||
```bash
|
||||
ansible-playbook \
|
||||
-i ansible/inventory.yaml \
|
||||
active/software_ai_stack/install_ai_text_stack.yaml
|
||||
```
|
||||
|
||||
### Image Stack
|
||||
|
||||
```bash
|
||||
ansible-playbook \
|
||||
-i ansible/inventory.yaml \
|
||||
active/software_ai_stack/install_ai_image_stack.yaml
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
### Create the AI user
|
||||
@@ -186,8 +146,7 @@ rsync -av --progress /home/ai/models/ /srv/models/
|
||||
In my completely subjective opinion: 5 bit quant is usually the sweet spot for
|
||||
unsloth models. Q5_K_S is usually just fine.
|
||||
|
||||
I usually download the F16 mmproj files. This is also completely subjective.
|
||||
BF16 is fine. F32 is overkill.
|
||||
I usually download the BF16 mmproj files. This is also completely subjective.
|
||||
|
||||
#### Text models
|
||||
|
||||
@@ -391,20 +350,43 @@ podman run \
|
||||
--name llama-server-demo \
|
||||
--device=/dev/kfd \
|
||||
--device=/dev/dri \
|
||||
-v /home/ai/models/text:/models:z \
|
||||
-v $(pwd):/models:z \
|
||||
-p 8010:8000 \
|
||||
--ipc host \
|
||||
localhost/llama-cpp-vulkan:latest \
|
||||
--host 0.0.0.0 \
|
||||
--port 8000 \
|
||||
-c 128000 \
|
||||
--perf \
|
||||
--n-gpu-layers all \
|
||||
--jinja \
|
||||
--models-max 1 \
|
||||
--models-dir /models \
|
||||
--chat-template-kwargs '{"enable_thinking": false}' \
|
||||
-m /models/qwen3.5-35b-a3b
|
||||
--model /models/gemma-4-E4B-it-qat-UD-Q4_K_XL.gguf \
|
||||
--mmproj /models/mmproj-F16.gguf \
|
||||
--temp 1.0 \
|
||||
--top-p 0.95 \
|
||||
--top-k 64 \
|
||||
--chat-template-kwargs '{"enable_thinking":true}'
|
||||
|
||||
podman run \
|
||||
--rm \
|
||||
--name llama-server-demo \
|
||||
--device=/dev/kfd \
|
||||
--device=/dev/dri \
|
||||
-v $(pwd):/models:z \
|
||||
-p 8010:8000 \
|
||||
--ipc host \
|
||||
localhost/llama-cpp-vulkan:latest \
|
||||
--host 0.0.0.0 \
|
||||
--port 8000 \
|
||||
--perf \
|
||||
--n-gpu-layers all \
|
||||
--jinja \
|
||||
--model /models/Qwen3.5-9B-UD-Q4_K_XL.gguf \
|
||||
--mmproj /models/mmproj-BF16.gguf \
|
||||
--temp 0.6 \
|
||||
--top-p 0.95 \
|
||||
--top-k 20 \
|
||||
--min-p 0.00 \
|
||||
--chat-template-kwargs '{"enable_thinking":true}'
|
||||
```
|
||||
|
||||
Embedding models
|
||||
@@ -595,96 +577,7 @@ podman run \
|
||||
-p 4000:4000
|
||||
```
|
||||
|
||||
## Install Services with Quadlets
|
||||
|
||||
### API Keys
|
||||
|
||||
```bash
|
||||
mkdir -p /home/ai/.llama-api
|
||||
touch /home/ai/.llama-api/keys.env
|
||||
chmod 600 /home/ai/.llama-api/keys.env
|
||||
vim /home/ai/.llama-api/keys.env
|
||||
|
||||
LLAMA_API_KEY=
|
||||
|
||||
# Generate keys and append to file, then comma separate the keys
|
||||
openssl rand -base64 48 >> keys.env
|
||||
openssl rand -base64 48 >> keys.env
|
||||
openssl rand -base64 48 >> keys.env
|
||||
```
|
||||
|
||||
### Internal and External Pods
|
||||
|
||||
These will be used to restrict internet access to our llama.cpp and
|
||||
stable-diffusion.cpp services while allowing the frontend services to
|
||||
communicate with those containers.
|
||||
|
||||
```bash
|
||||
scp -r active/software_ai_stack/ai-internal.* deskwork-ai:.config/containers/systemd/
|
||||
ssh deskwork-ai
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user start ai-internal-pod.service
|
||||
```
|
||||
|
||||
### Llama CPP Server (Port 8000)
|
||||
|
||||
Installs the llama.cpp server to run our text models.
|
||||
|
||||
```bash
|
||||
scp -r active/software_ai_stack/llama-think.container deskwork-ai:.config/containers/systemd/
|
||||
ssh deskwork-ai
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user restart ai-internal-pod.service
|
||||
```
|
||||
|
||||
### Llama CPP Embedding Server (Port 8001)
|
||||
|
||||
Installs the llama.cpp server to run our embedding models
|
||||
|
||||
```bash
|
||||
scp -r active/software_ai_stack/llama-embed.container deskwork-ai:.config/containers/systemd/
|
||||
ssh deskwork-ai
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user restart ai-internal-pod.service
|
||||
```
|
||||
|
||||
### Llama CPP Instruct Server (Port 8002)
|
||||
|
||||
Installs the llama.cpp server to run a constant instruct (no thinking) model for quick replies
|
||||
|
||||
```bash
|
||||
scp -r active/software_ai_stack/llama-instruct.container deskwork-ai:.config/containers/systemd/
|
||||
ssh deskwork-ai
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user restart ai-internal-pod.service
|
||||
```
|
||||
|
||||
### Stable Diffusion CPP (Port 1234 and 1235)
|
||||
|
||||
Installs the stable-diffusion.cpp server to run our image models.
|
||||
|
||||
```bash
|
||||
scp -r active/software_ai_stack/quadlets_stable_diffusion/* deskwork-ai:.config/containers/systemd/
|
||||
ssh deskwork-ai
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user restart ai-internal-pod.service
|
||||
```
|
||||
|
||||
### Open Webui (Port 8080)
|
||||
|
||||
Installs the open webui frontend.
|
||||
|
||||
```bash
|
||||
scp -r active/software_ai_stack/quadlets_openwebui/* deskwork-ai:.config/containers/systemd/
|
||||
ssh deskwork-ai
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user restart ai-external-pod.service
|
||||
```
|
||||
|
||||
Note, all services will be available at `host.containers.internal`. So llama.cpp
|
||||
will be up at `http://host.containers.internal:8000`.
|
||||
|
||||
### Install the update script
|
||||
## Install the update script
|
||||
|
||||
```bash
|
||||
# 1. Builds the latest llama.cpp and stable-diffusion.cpp
|
||||
@@ -696,18 +589,6 @@ chmod +x update-script.sh
|
||||
./update-script.sh
|
||||
```
|
||||
|
||||
### Install Guest Open Webui with Start/Stop Services
|
||||
|
||||
Optionally install a guest openwebui service.
|
||||
|
||||
```bash
|
||||
scp -r active/software_ai_stack/systemd/. deskwork-ai:.config/systemd/user/
|
||||
ssh deskwork-ai
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user enable open-webui-guest-start.timer
|
||||
systemctl --user enable open-webui-guest-stop.timer
|
||||
```
|
||||
|
||||
## Benchmark Results
|
||||
|
||||
Benchmarks are run with [unsloth gpt-oss-20b Q8_0](https://huggingface.co/unsloth/gpt-oss-20b-GGUF/blob/main/gpt-oss-20b-Q8_0.gguf)
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
- name: Create Deskwork AI Stack
|
||||
hosts: deskwork-ai
|
||||
tasks:
|
||||
- name: Create /home/ai/.config/containers/systemd
|
||||
ansible.builtin.file:
|
||||
path: /home/ai/.config/containers/systemd
|
||||
state: directory
|
||||
mode: "0755"
|
||||
- name: Copy Quadlets
|
||||
template:
|
||||
src: "{{ item }}"
|
||||
dest: "/home/ai/.config/containers/systemd/{{ item }}"
|
||||
loop:
|
||||
- ai-internal.network
|
||||
- ai-internal.pod
|
||||
- stable-diffusion-gen-server.container
|
||||
- stable-diffusion-edit-server.container
|
||||
- name: Reload and start the ai-internal-pod service
|
||||
ansible.builtin.systemd_service:
|
||||
state: restarted
|
||||
name: ai-internal-pod.service
|
||||
daemon_reload: true
|
||||
scope: user
|
||||
@@ -1,24 +0,0 @@
|
||||
- name: Create Deskwork AI Stack
|
||||
hosts: deskwork-ai
|
||||
tasks:
|
||||
- name: Create /home/ai/.config/containers/systemd
|
||||
ansible.builtin.file:
|
||||
path: /home/ai/.config/containers/systemd
|
||||
state: directory
|
||||
mode: "0755"
|
||||
- name: Copy Quadlets
|
||||
template:
|
||||
src: "{{ item }}"
|
||||
dest: "/home/ai/.config/containers/systemd/{{ item }}"
|
||||
loop:
|
||||
- ai-internal.network
|
||||
- ai-internal.pod
|
||||
- llama-embed.container
|
||||
- llama-instruct.container
|
||||
- llama-tool.container
|
||||
- name: Reload and start the ai-internal-pod service
|
||||
ansible.builtin.systemd_service:
|
||||
state: restarted
|
||||
name: ai-internal-pod.service
|
||||
daemon_reload: true
|
||||
scope: user
|
||||
@@ -1,22 +0,0 @@
|
||||
- name: Create Deskwork AI Stack
|
||||
hosts: driveripper-ai
|
||||
tasks:
|
||||
- name: Create /home/ai/.config/containers/systemd
|
||||
ansible.builtin.file:
|
||||
path: /home/ai/.config/containers/systemd
|
||||
state: directory
|
||||
mode: "0755"
|
||||
- name: Copy Quadlets
|
||||
template:
|
||||
src: "{{ item }}"
|
||||
dest: "/home/ai/.config/containers/systemd/{{ item }}"
|
||||
loop:
|
||||
- ai-internal.network
|
||||
- ai-internal.pod
|
||||
- llama-turbo.container
|
||||
- name: Reload and start the ai-internal-pod service
|
||||
ansible.builtin.systemd_service:
|
||||
state: restarted
|
||||
name: ai-internal-pod.service
|
||||
daemon_reload: true
|
||||
scope: user
|
||||
@@ -1,46 +0,0 @@
|
||||
[Unit]
|
||||
Description=A Llama CPP Server For Embedding Models
|
||||
|
||||
[Container]
|
||||
# Shared AI internal pod without internet access
|
||||
Pod=ai-internal.pod
|
||||
|
||||
# Image is built locally via podman build
|
||||
Image=localhost/llama-cpp-vulkan:latest
|
||||
|
||||
# Downloaded models volume
|
||||
Volume=/home/ai/models/embedding:/models:z
|
||||
|
||||
# GPU Device
|
||||
AddDevice=/dev/kfd
|
||||
AddDevice=/dev/dri
|
||||
|
||||
# Server command
|
||||
Exec=--port 8001 \
|
||||
-c 0 \
|
||||
-b 1024 \
|
||||
-ub 1024 \
|
||||
--perf \
|
||||
--n-gpu-layers all \
|
||||
--embedding \
|
||||
-m /models/emebeddinggemma-300m/embeddinggemma-300M-BF16.gguf \
|
||||
--alias embed
|
||||
|
||||
# Health Check
|
||||
HealthCmd=CMD-SHELL curl --fail http://127.0.0.1:8001/health || exit 1
|
||||
HealthInterval=10s
|
||||
HealthRetries=3
|
||||
HealthStartPeriod=10s
|
||||
HealthTimeout=30s
|
||||
HealthOnFailure=kill
|
||||
|
||||
# EnvironmentFile=/home/ai/.llama-api/keys.env
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
# Extend Timeout to allow time to pull the image
|
||||
TimeoutStartSec=900
|
||||
|
||||
[Install]
|
||||
# Start by default on boot
|
||||
WantedBy=multi-user.target default.target
|
||||
@@ -1,54 +0,0 @@
|
||||
[Unit]
|
||||
Description=A Llama CPP Server Running a Non-Reasoning Model
|
||||
|
||||
[Container]
|
||||
# Shared AI internal pod without internet access
|
||||
Pod=ai-internal.pod
|
||||
|
||||
# Image is built locally via podman build
|
||||
Image=localhost/llama-cpp-vulkan:latest
|
||||
|
||||
# Downloaded models volume
|
||||
Volume=/home/ai/models/text:/models:z
|
||||
|
||||
# GPU Device
|
||||
AddDevice=/dev/kfd
|
||||
AddDevice=/dev/dri
|
||||
|
||||
# Server command
|
||||
Exec=--port 8002 \
|
||||
-c 262144 \
|
||||
-n 32768 \
|
||||
--temp 0.7 \
|
||||
--top-p 0.8 \
|
||||
--top-k 20 \
|
||||
--presence-penalty 1.5 \
|
||||
--min-p 0.00 \
|
||||
--perf \
|
||||
--n-gpu-layers all \
|
||||
-fa on \
|
||||
--spec-type draft-mtp --spec-draft-n-max 2 \
|
||||
--jinja \
|
||||
--chat-template-kwargs '{"preserve_thinking": false, "enable_thinking": false}' \
|
||||
-m /models/qwen3.6-35b-a3b-mtp/Qwen3.6-35B-A3B-UD-Q5_K_M.gguf \
|
||||
--mmproj /models/qwen3.6-35b-a3b-mtp/mmproj-F16.gguf \
|
||||
--alias instruct
|
||||
|
||||
# Health Check
|
||||
HealthCmd=CMD-SHELL curl --fail http://127.0.0.1:8002/health || exit 1
|
||||
HealthInterval=10s
|
||||
HealthRetries=3
|
||||
HealthStartPeriod=10s
|
||||
HealthTimeout=30s
|
||||
HealthOnFailure=kill
|
||||
|
||||
# EnvironmentFile=/home/ai/.llama-api/keys.env
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
# Extend Timeout to allow time to pull the image
|
||||
TimeoutStartSec=900
|
||||
|
||||
[Install]
|
||||
# Start by default on boot
|
||||
WantedBy=multi-user.target default.target
|
||||
@@ -1,57 +0,0 @@
|
||||
[Unit]
|
||||
Description=A Llama CPP Server Running a Reasoning Model
|
||||
|
||||
[Container]
|
||||
# Shared AI internal pod without internet access
|
||||
Pod=ai-internal.pod
|
||||
|
||||
# Image is built locally via podman build
|
||||
# latest-mtp is for mtp testing
|
||||
# latest is main branch
|
||||
Image=localhost/llama-cpp-vulkan:latest
|
||||
|
||||
# Downloaded models volume
|
||||
Volume=/home/ai/models/text:/models:z
|
||||
|
||||
# GPU Device
|
||||
AddDevice=/dev/kfd
|
||||
AddDevice=/dev/dri
|
||||
|
||||
# Server command
|
||||
Exec=--port 8003 \
|
||||
-c 262144 \
|
||||
-n 32768 \
|
||||
--temp 0.6 \
|
||||
--top-p 0.95 \
|
||||
--top-k 20 \
|
||||
--min-p 0.0 \
|
||||
--presence-penalty 0.0 \
|
||||
--repeat-penalty 1.0 \
|
||||
--reasoning-budget 4096 \
|
||||
--perf \
|
||||
--n-gpu-layers all \
|
||||
-fa on \
|
||||
--spec-type draft-mtp --spec-draft-n-max 2 \
|
||||
--jinja \
|
||||
--chat-template-kwargs '{"preserve_thinking": false}' \
|
||||
--chat-template-file /models/qwen3.6-35b-a3b-mtp/chat_template.jinja \
|
||||
-m /models/qwen3.6-35b-a3b-mtp/Qwen3.6-35B-A3B-UD-Q5_K_M.gguf \
|
||||
--mmproj /models/qwen3.6-35b-a3b-mtp/mmproj-F16.gguf \
|
||||
--alias tool
|
||||
|
||||
# Health Check
|
||||
HealthCmd=CMD-SHELL curl --fail http://127.0.0.1:8003/health || exit 1
|
||||
HealthInterval=10s
|
||||
HealthRetries=3
|
||||
HealthStartPeriod=10s
|
||||
HealthTimeout=30s
|
||||
HealthOnFailure=kill
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
# Extend Timeout to allow time to pull the image
|
||||
TimeoutStartSec=900
|
||||
|
||||
[Install]
|
||||
# Start by default on boot
|
||||
WantedBy=multi-user.target default.target
|
||||
@@ -1,57 +0,0 @@
|
||||
[Unit]
|
||||
Description=A Llama CPP Server Running a Reasoning Model
|
||||
|
||||
[Container]
|
||||
# Shared AI internal pod without internet access
|
||||
Pod=ai-internal.pod
|
||||
|
||||
# Image is built locally via podman build
|
||||
# latest-mtp is for mtp testing
|
||||
# latest is main branch
|
||||
Image=localhost/llama-cpp-vulkan:latest
|
||||
|
||||
# Downloaded models volume
|
||||
Volume=/home/ai/models/text:/models:z
|
||||
|
||||
# GPU Device
|
||||
AddDevice=/dev/kfd
|
||||
AddDevice=/dev/dri
|
||||
|
||||
# Server command
|
||||
Exec=--port 8000 \
|
||||
-c 262144 \
|
||||
-n 32768 \
|
||||
--temp 0.6 \
|
||||
--top-p 0.95 \
|
||||
--top-k 20 \
|
||||
--min-p 0.0 \
|
||||
--presence-penalty 0.0 \
|
||||
--repeat-penalty 1.0 \
|
||||
--reasoning-budget 4096 \
|
||||
--perf \
|
||||
--n-gpu-layers all \
|
||||
-fa on \
|
||||
--spec-type draft-mtp --spec-draft-n-max 2 \
|
||||
--jinja \
|
||||
--chat-template-kwargs '{"preserve_thinking": true}' \
|
||||
--chat-template-file /models/qwen3.6-35b-a3b-mtp/chat_template.jinja \
|
||||
-m /models/qwen3.6-35b-a3b-mtp/Qwen3.6-35B-A3B-UD-Q5_K_M.gguf \
|
||||
--mmproj /models/qwen3.6-35b-a3b-mtp/mmproj-F16.gguf \
|
||||
--alias turbo
|
||||
|
||||
# Health Check
|
||||
HealthCmd=CMD-SHELL curl --fail http://127.0.0.1:8000/health || exit 1
|
||||
HealthInterval=10s
|
||||
HealthRetries=3
|
||||
HealthStartPeriod=10s
|
||||
HealthTimeout=30s
|
||||
HealthOnFailure=kill
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
# Extend Timeout to allow time to pull the image
|
||||
TimeoutStartSec=900
|
||||
|
||||
[Install]
|
||||
# Start by default on boot
|
||||
WantedBy=multi-user.target default.target
|
||||
@@ -1,24 +0,0 @@
|
||||
[Unit]
|
||||
Description=An Open Webui Frontend for Local AI Services
|
||||
|
||||
[Container]
|
||||
# Shared AI external pod
|
||||
PublishPort=8080:8080
|
||||
|
||||
# Open Webui base image
|
||||
Image=ghcr.io/open-webui/open-webui:main
|
||||
|
||||
# Nothing too complicated here. Open Webui will basically configure itself.
|
||||
Volume=open-webui-data:/app/backend/data
|
||||
|
||||
# WEBUI_SECRET_KEY is required to prevent logout on Restart
|
||||
EnvironmentFile=/home/ai/.env/open-webui-env
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
# Extend Timeout to allow time to pull the image
|
||||
TimeoutStartSec=900
|
||||
|
||||
[Install]
|
||||
# Start by default on boot
|
||||
WantedBy=multi-user.target default.target
|
||||
@@ -1,44 +0,0 @@
|
||||
[Unit]
|
||||
Description=A Stable Diffusion CPP Server for Editing Images
|
||||
|
||||
[Container]
|
||||
# Shared AI internal pod without internet access
|
||||
Pod=ai-internal.pod
|
||||
|
||||
# Vulkan image for AMD GPU
|
||||
Image=localhost/stable-diffusion-cpp:latest
|
||||
|
||||
# Shared models directory
|
||||
Volume=/home/ai/models:/models:z
|
||||
|
||||
# GPU Device
|
||||
AddDevice=/dev/kfd
|
||||
AddDevice=/dev/dri
|
||||
|
||||
# Override entrypoint to use server
|
||||
Entrypoint=/sd-server
|
||||
|
||||
# Server args
|
||||
Exec=-l 0.0.0.0 \
|
||||
--listen-port 1235 \
|
||||
--diffusion-model /models/image/flux2-klein/flux-2-klein-9b-Q8_0.gguf \
|
||||
--vae /models/image/flux2-klein/ae.safetensors \
|
||||
--llm /models/image/flux2-klein/Qwen3-8B-Q4_K_M.gguf \
|
||||
-v \
|
||||
--sampling-method euler \
|
||||
--cfg-scale 1.0 \
|
||||
--vae-conv-direct \
|
||||
--diffusion-conv-direct \
|
||||
--fa \
|
||||
--mmap \
|
||||
--seed -1 \
|
||||
--steps 5
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
# Extend Timeout to allow time to pull the image
|
||||
TimeoutStartSec=900
|
||||
|
||||
[Install]
|
||||
# Start by default on boot
|
||||
WantedBy=multi-user.target default.target
|
||||
@@ -1,43 +0,0 @@
|
||||
[Unit]
|
||||
Description=A Stable Diffusion CPP Server for Generating Images
|
||||
|
||||
[Container]
|
||||
# Shared AI internal pod without internet access
|
||||
Pod=ai-internal.pod
|
||||
|
||||
# Vulkan image for AMD GPU
|
||||
Image=localhost/stable-diffusion-cpp:latest
|
||||
|
||||
# Shared models directory
|
||||
Volume=/home/ai/models:/models:z
|
||||
|
||||
# GPU Device
|
||||
AddDevice=/dev/kfd
|
||||
AddDevice=/dev/dri
|
||||
|
||||
# Override entrypoint to use server
|
||||
Entrypoint=/sd-server
|
||||
|
||||
# Server args
|
||||
Exec=-l 0.0.0.0 \
|
||||
--listen-port 1234 \
|
||||
--diffusion-model /models/image/z-turbo/z_image_turbo-Q8_0.gguf \
|
||||
--vae /models/image/z-turbo/ae.safetensors \
|
||||
--llm /models/image/z-turbo/Qwen3-4B-Instruct-2507-Q4_K_M.gguf \
|
||||
-v \
|
||||
--cfg-scale 1.0 \
|
||||
--vae-conv-direct \
|
||||
--diffusion-conv-direct \
|
||||
--fa \
|
||||
--mmap \
|
||||
--seed -1 \
|
||||
--steps 8
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
# Extend Timeout to allow time to pull the image
|
||||
TimeoutStartSec=900
|
||||
|
||||
[Install]
|
||||
# Start by default on boot
|
||||
WantedBy=multi-user.target default.target
|
||||
@@ -1,8 +0,0 @@
|
||||
[Unit]
|
||||
Description=Start open-webui-guest on schedule
|
||||
Wants=open-webui-guest.service
|
||||
After=open-webui-guest.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/bin/systemctl --user start open-webui-guest.service
|
||||
@@ -1,10 +0,0 @@
|
||||
[Unit]
|
||||
Description=Timer to start open-webui-guest at 08:00 Mon‑Fri
|
||||
|
||||
[Timer]
|
||||
OnCalendar=Mon..Fri *-*-* 08:00:00
|
||||
Persistent=true
|
||||
Unit=open-webui-guest-start.service
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
@@ -1,7 +0,0 @@
|
||||
[Unit]
|
||||
Description=Stop open-webui-guest on schedule
|
||||
After=open-webui-guest.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/bin/systemctl --user stop open-webui-guest.service
|
||||
@@ -1,10 +0,0 @@
|
||||
[Unit]
|
||||
Description=Timer to stop open-webui-guest at 18:00 Mon‑Fri
|
||||
|
||||
[Timer]
|
||||
OnCalendar=Mon..Fri *-*-* 18:00:00
|
||||
Persistent=true
|
||||
Unit=open-webui-guest-stop.service
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
@@ -0,0 +1,5 @@
|
||||
# Konsole
|
||||
|
||||
## Tips
|
||||
|
||||
- To open new tabs with the same profile as the current tab, assign the "clone tab" keyboard shortcut to <ctrl>+<shift>+<T>.
|
||||
@@ -0,0 +1,79 @@
|
||||
name = "fedora44-base"
|
||||
description = "Fedora Base Installation"
|
||||
version = "0.2.3"
|
||||
distro = "fedora-44"
|
||||
modules = []
|
||||
groups = []
|
||||
|
||||
[customizations]
|
||||
hostname = "fedora44-base"
|
||||
|
||||
[customizations.timezone]
|
||||
timezone = "America/New_York"
|
||||
|
||||
[[customizations.disk.partitions]]
|
||||
type = "plain"
|
||||
label = "root"
|
||||
mountpoint = "/"
|
||||
fs_type = "ext4"
|
||||
minsize = "128 GiB"
|
||||
|
||||
[customizations.firewall.services]
|
||||
enabled = ["ssh", "cockpit"]
|
||||
|
||||
[customizations.services]
|
||||
enabled = ["sshd", "cockpit.socket"]
|
||||
|
||||
[[packages]]
|
||||
name = "bash-completion"
|
||||
version = "*"
|
||||
|
||||
[[packages]]
|
||||
name = "tmux"
|
||||
version = "*"
|
||||
|
||||
[[packages]]
|
||||
name = "openssh-server"
|
||||
version = "*"
|
||||
|
||||
[[packages]]
|
||||
name = "vim"
|
||||
version = "*"
|
||||
|
||||
[[packages]]
|
||||
name = "git"
|
||||
version = "*"
|
||||
|
||||
[[packages]]
|
||||
name = "cockpit"
|
||||
version = "*"
|
||||
|
||||
[[packages]]
|
||||
name = "borgbackup"
|
||||
version = "*"
|
||||
|
||||
[[packages]]
|
||||
name = "policycoreutils-python-utils"
|
||||
version = "*"
|
||||
|
||||
[[packages]]
|
||||
name = "systemd-container"
|
||||
version = "*"
|
||||
|
||||
[[customizations.files]]
|
||||
path = "/root/.inputrc"
|
||||
mode = "0644"
|
||||
user = "root"
|
||||
group = "root"
|
||||
data = """
|
||||
"\\C-h": backward-kill-word
|
||||
"""
|
||||
|
||||
[[customizations.files]]
|
||||
path = "/root/.ssh/authorized_keys"
|
||||
mode = "0600"
|
||||
user = "root"
|
||||
group = "root"
|
||||
data = """
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGQa781Qj8mNlUdRquFFqg0O2ornG9SBHe705y4+1vPI ssh@ducoterra.net
|
||||
"""
|
||||
@@ -29,6 +29,8 @@ dnf install -y osbuild-composer composer-cli
|
||||
systemctl enable --now osbuild-composer.socket
|
||||
|
||||
# Optional: add your user to the weldr group
|
||||
# On fedora kinoite (atomic) you'll need to add the group manually for now
|
||||
grep -E '^weldr:' /usr/lib/group | sudo tee -a /etc/group
|
||||
sudo usermod -aG weldr $USER
|
||||
|
||||
# Optional: cockpit dependency
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
# Podman
|
||||
|
||||
## Networks
|
||||
|
||||
```bash
|
||||
# Create a podman network to group shared containers
|
||||
podman network create test1
|
||||
# Create an iperf3 container running on that network
|
||||
podman run -d --name iperf3 --network test1 docker.io/networkstatic/iperf3 -s
|
||||
# A container running in the same network can reference another pod by its name
|
||||
podman run --rm -it --network test1 docker.io/networkstatic/iperf3 -c iperf3
|
||||
# A container outside the created network can't talk to other containers
|
||||
# This will fail
|
||||
podman run --rm -it docker.io/networkstatic/iperf3 -c iperf3
|
||||
```
|
||||
|
||||
## Healthchecks
|
||||
|
||||
TCP Healthchecks can be accomplished with netcat (netcat-openbsd)
|
||||
|
||||
```bash
|
||||
podman run -d --name iperf3 \
|
||||
--health-cmd='CMD-SHELL iperf3 -n 1 -c localhost' \
|
||||
--health-interval=10s --health-timeout=5s --health-retries=3 \
|
||||
docker.io/networkstatic/iperf3 iperf3 -s
|
||||
```
|
||||
|
||||
HTTP Healthchecks can be accomplished with curl
|
||||
|
||||
```bash
|
||||
podman run -d --name nginx \
|
||||
--health-cmd='CMD-SHELL curl --fail http://127.0.0.1:80 || exit 1' \
|
||||
--health-interval=10s --health-timeout=5s --health-retries=3 \
|
||||
docker.io/nginx
|
||||
```
|
||||
|
||||
Check healthchecks with
|
||||
|
||||
```bash
|
||||
podman --log-level debug healthcheck run <container>
|
||||
```
|
||||
|
||||
## Volume Import and Export
|
||||
|
||||
```bash
|
||||
podman volume export myvol --output myvol.tar
|
||||
podman volume import myvol test.tar
|
||||
```
|
||||
@@ -30,11 +30,17 @@ sudo smbpasswd -a sambauser
|
||||
## Create a SMB Share
|
||||
|
||||
```bash
|
||||
# Add the share user
|
||||
sudo useradd -r -M -s /usr/sbin/nologin sambauser
|
||||
sudo smbpasswd -a sambauser
|
||||
|
||||
# Create share
|
||||
mkdir /srv/smb/sambauser
|
||||
sudo mkdir /srv/smb/sambauser
|
||||
sudo chown -R sambauser:sambauser /srv/smb/sambauser
|
||||
sudo chmod -R 2770 /srv/samba/sambauser
|
||||
|
||||
# Set proper selinux labels for samba
|
||||
sudo semanage fcontext --add --type "samba_share_t" "/srv/smb(/.*)?"
|
||||
sudo semanage fcontext --add --type "samba_share_t" "/srv/smb/sambauser"
|
||||
|
||||
# Run restorecon at the root of the btrfs subvolume
|
||||
sudo restorecon -FRv /srv
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
FROM registry.fedoraproject.org/fedora-toolbox:44
|
||||
|
||||
COPY vscodium.repo /etc/yum.repos.d/vscodium.repo
|
||||
|
||||
RUN dnf install -y \
|
||||
vim git btop \
|
||||
virt-manager virt-install \
|
||||
podman podman-compose \
|
||||
wl-clipboard \
|
||||
jq yq rg \
|
||||
kubectl helm \
|
||||
pinentry \
|
||||
pandoc \
|
||||
iperf3 \
|
||||
nethogs \
|
||||
ansible \
|
||||
tmux \
|
||||
wine \
|
||||
unzip \
|
||||
ImageMagick \
|
||||
go \
|
||||
rust rustup \
|
||||
git git-lfs \
|
||||
net-tools \
|
||||
texlive-latex texlive-scheme-full \
|
||||
pwgen \
|
||||
reptyr \
|
||||
netcat \
|
||||
p7zip \
|
||||
make \
|
||||
gcc \
|
||||
codium \
|
||||
pass
|
||||
@@ -0,0 +1,7 @@
|
||||
# Toolbox
|
||||
|
||||
## How to Use
|
||||
|
||||
```bash
|
||||
podman build -t localhost/fedora-toolbox:44 active/software_toolbx
|
||||
```
|
||||
@@ -0,0 +1,9 @@
|
||||
[gitlab.com_paulcarroty_vscodium_repo]
|
||||
name=gitlab.com_paulcarroty_vscodium_repo
|
||||
baseurl=https://paulcarroty.gitlab.io/vscodium-deb-rpm-repo/rpms/
|
||||
enabled=1
|
||||
gpgcheck=1
|
||||
repo_gpgcheck=1
|
||||
gpgkey=https://gitlab.com/paulcarroty/vscodium-deb-rpm-repo/raw/master/pub.gpg
|
||||
metadata_expire=1h
|
||||
|
||||
@@ -40,7 +40,6 @@ Virtual Machine Management
|
||||
<https://libvirt.org/uri.html#ssh-transport>
|
||||
|
||||
### Configuring Aliases
|
||||
|
||||
1. Edit `~/.config/libvirt/libvirt.conf`
|
||||
2. Add your aliases
|
||||
|
||||
|
||||
@@ -1,24 +1,19 @@
|
||||
import json
|
||||
import os
|
||||
import smtplib
|
||||
import ssl
|
||||
import subprocess
|
||||
from email.message import EmailMessage
|
||||
from pathlib import Path
|
||||
from typing import Iterable, TypedDict, cast
|
||||
|
||||
import requests
|
||||
from dotenv import dotenv_values, load_dotenv
|
||||
from openai import OpenAI
|
||||
from openai.types.chat import ChatCompletionMessageParam, ChatCompletionToolUnionParam
|
||||
|
||||
|
||||
class AWS_SES_DOTENV(TypedDict):
|
||||
USER: str
|
||||
PASSWORD: str
|
||||
ENDPOINT: str
|
||||
TLS_PORT: str
|
||||
SENDER: str
|
||||
RECEIVER: str
|
||||
class NTFY_DOTENV(TypedDict):
|
||||
TOKEN: str
|
||||
|
||||
|
||||
class ToolCallController:
|
||||
@@ -209,39 +204,30 @@ def smartctl(device_path: str) -> str:
|
||||
return output
|
||||
|
||||
|
||||
def load_ses_creds() -> AWS_SES_DOTENV:
|
||||
def load_ntfy_creds() -> NTFY_DOTENV:
|
||||
ses_dotenv_location = Path(os.getenv("HOME", "/root"), ".env/aws_ses")
|
||||
print(f"Loading env from {ses_dotenv_location}")
|
||||
raw_values = dotenv_values(ses_dotenv_location)
|
||||
if raw_values:
|
||||
aws_ses_config = cast(AWS_SES_DOTENV, raw_values)
|
||||
ntfy_config = cast(NTFY_DOTENV, raw_values)
|
||||
# print(f"AWS SES Credentials loaded: {aws_ses_config}")
|
||||
return aws_ses_config
|
||||
print("No email credentials supplied. Exiting.")
|
||||
return ntfy_config
|
||||
print("No ntfy credentials supplied. Exiting.")
|
||||
exit(1)
|
||||
|
||||
|
||||
def alert_user(message: str) -> str:
|
||||
ses_config = load_ses_creds()
|
||||
port = int(ses_config["TLS_PORT"])
|
||||
user = ses_config["USER"]
|
||||
password = ses_config["PASSWORD"]
|
||||
sender = ses_config["SENDER"]
|
||||
receiver = ses_config["RECEIVER"]
|
||||
ntfy_creds = load_ntfy_creds()
|
||||
|
||||
# Create a secure SSL context
|
||||
context = ssl.create_default_context()
|
||||
|
||||
msg = EmailMessage()
|
||||
msg["Subject"] = "Agent Disk Report"
|
||||
msg["From"] = sender
|
||||
msg["To"] = receiver
|
||||
msg.set_content(message)
|
||||
|
||||
with smtplib.SMTP_SSL(ses_config["ENDPOINT"], port, context=context) as server:
|
||||
server.login(user, password)
|
||||
result = server.send_message(msg)
|
||||
print(result)
|
||||
requests.post(
|
||||
"https://ntfy.reeseapps.com/servers",
|
||||
data=message,
|
||||
headers={
|
||||
"Authorization": f"Bearer {ntfy_creds['TOKEN']}",
|
||||
"Title": "Agent Disk Report",
|
||||
"Priority": "default",
|
||||
},
|
||||
)
|
||||
|
||||
return "Sent."
|
||||
|
||||
@@ -322,13 +308,14 @@ def run_conversation(user_message: str, max_tool_calls=10):
|
||||
if __name__ == "__main__":
|
||||
load_dotenv()
|
||||
api_key = os.getenv("OPENAI_API_KEY", "")
|
||||
client = OpenAI(base_url="https://llama-think.reeselink.com", api_key=api_key)
|
||||
client = OpenAI(base_url="https://aipi.reeseapps.com", api_key=api_key)
|
||||
# Example usage
|
||||
print(
|
||||
run_conversation(
|
||||
"Check the btrfs pools on this system. Take the appropriate action if any pools aren't "
|
||||
"healthy. Don't run scrubs unless necessary. Also check the btrfs pool space and report "
|
||||
"if any are getting full. At the very end of performing your checks send a single, "
|
||||
"concise message to the user explaining what you did and what concerns you might have."
|
||||
"concise message to the user via the alert_user tool explaining what you did and "
|
||||
"what concerns you might have."
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# Proxy Automation
|
||||
|
||||
1. Create or update the reeselink.com address in Unifi
|
||||
2. Create or update the entry in the ddns values
|
||||
3. Create or update the entry in the caddy values
|
||||
@@ -1 +0,0 @@
|
||||
3.13
|
||||
@@ -1,9 +0,0 @@
|
||||
# Update
|
||||
|
||||
Updates every server in the fleet
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
uv run automations/updates/main.py
|
||||
```
|
||||
@@ -1,64 +0,0 @@
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
import yaml
|
||||
from tqdm import tqdm
|
||||
|
||||
|
||||
def main():
|
||||
print("Retrieving hosts")
|
||||
with open("ansible/inventory.yaml", "r") as f:
|
||||
all_hosts = yaml.load(f, yaml.SafeLoader)
|
||||
fedora_hosts = all_hosts["fedora"]["hosts"].keys()
|
||||
|
||||
with open("update.log", "w") as log_file:
|
||||
for _, host in enumerate(tqdm(fedora_hosts, desc="Running system updates")):
|
||||
log_file.write(f"Updating {host}\n")
|
||||
log_file.flush()
|
||||
try:
|
||||
subprocess.run(
|
||||
["ssh", host, "dnf", "upgrade", "-y"],
|
||||
stdout=log_file,
|
||||
stderr=log_file,
|
||||
check=True,
|
||||
)
|
||||
except Exception as e:
|
||||
log_file.write(f"Couldn't connect to {host}. Skipping...\n")
|
||||
continue
|
||||
log_file.flush()
|
||||
log_file.write(f"Rebooting {host}\n")
|
||||
log_file.flush()
|
||||
subprocess.run(
|
||||
["ssh", host, "reboot"],
|
||||
stdout=log_file,
|
||||
stderr=log_file,
|
||||
check=True,
|
||||
)
|
||||
time.sleep(5) # wait for reboot to take effect
|
||||
booted = False
|
||||
max_attempts = 10
|
||||
cur_attempts = 0
|
||||
while max_attempts > cur_attempts and not booted:
|
||||
try:
|
||||
subprocess.run(
|
||||
["ssh", host, "echo"],
|
||||
stdout=log_file,
|
||||
stderr=log_file,
|
||||
check=True,
|
||||
timeout=2,
|
||||
)
|
||||
log_file.write(f"{host} booted!\n")
|
||||
log_file.flush()
|
||||
booted = True
|
||||
except Exception as e:
|
||||
cur_attempts += 1
|
||||
log_file.write(f"Waiting for {host} to reboot\n")
|
||||
log_file.flush()
|
||||
time.sleep(5)
|
||||
|
||||
if cur_attempts >= max_attempts:
|
||||
exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,12 +0,0 @@
|
||||
[project]
|
||||
name = "updates"
|
||||
version = "0.1.0"
|
||||
description = "Update servers based on ansible inventory"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.13"
|
||||
dependencies = [
|
||||
"pyyaml>=6.0.3",
|
||||
"tqdm>=4.67.3",
|
||||
"types-pyyaml>=6.0.12.20250915",
|
||||
"types-tqdm>=4.67.3.20260205",
|
||||
]
|
||||
Generated
-121
@@ -1,121 +0,0 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.13"
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "6.0.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tqdm"
|
||||
version = "4.67.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598, upload-time = "2026-02-03T17:35:53.048Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "types-pyyaml"
|
||||
version = "6.0.12.20250915"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7e/69/3c51b36d04da19b92f9e815be12753125bd8bc247ba0470a982e6979e71c/types_pyyaml-6.0.12.20250915.tar.gz", hash = "sha256:0f8b54a528c303f0e6f7165687dd33fafa81c807fcac23f632b63aa624ced1d3", size = 17522, upload-time = "2025-09-15T03:01:00.728Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/e0/1eed384f02555dde685fff1a1ac805c1c7dcb6dd019c916fe659b1c1f9ec/types_pyyaml-6.0.12.20250915-py3-none-any.whl", hash = "sha256:e7d4d9e064e89a3b3cae120b4990cd370874d2bf12fa5f46c97018dd5d3c9ab6", size = 20338, upload-time = "2025-09-15T03:00:59.218Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "types-requests"
|
||||
version = "2.32.4.20260107"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "urllib3" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0f/f3/a0663907082280664d745929205a89d41dffb29e89a50f753af7d57d0a96/types_requests-2.32.4.20260107.tar.gz", hash = "sha256:018a11ac158f801bfa84857ddec1650750e393df8a004a8a9ae2a9bec6fcb24f", size = 23165, upload-time = "2026-01-07T03:20:54.091Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/12/709ea261f2bf91ef0a26a9eed20f2623227a8ed85610c1e54c5805692ecb/types_requests-2.32.4.20260107-py3-none-any.whl", hash = "sha256:b703fe72f8ce5b31ef031264fe9395cac8f46a04661a79f7ed31a80fb308730d", size = 20676, upload-time = "2026-01-07T03:20:52.929Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "types-tqdm"
|
||||
version = "4.67.3.20260205"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "types-requests" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/53/46/790b9872523a48163bdda87d47849b4466017640e5259d06eed539340afd/types_tqdm-4.67.3.20260205.tar.gz", hash = "sha256:f3023682d4aa3bbbf908c8c6bb35f35692d319460d9bbd3e646e8852f3dd9f85", size = 17597, upload-time = "2026-02-05T04:03:19.721Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/da/7f761868dbaa328392356fab30c18ab90d14cce86b269e7e63328f29d4a3/types_tqdm-4.67.3.20260205-py3-none-any.whl", hash = "sha256:85c31731e81dc3c5cecc34c6c8b2e5166fafa722468f58840c2b5ac6a8c5c173", size = 23894, upload-time = "2026-02-05T04:03:18.48Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "updates"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "pyyaml" },
|
||||
{ name = "tqdm" },
|
||||
{ name = "types-pyyaml" },
|
||||
{ name = "types-tqdm" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "pyyaml", specifier = ">=6.0.3" },
|
||||
{ name = "tqdm", specifier = ">=4.67.3" },
|
||||
{ name = "types-pyyaml", specifier = ">=6.0.12.20250915" },
|
||||
{ name = "types-tqdm", specifier = ">=4.67.3.20260205" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.6.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" },
|
||||
]
|
||||
@@ -16,6 +16,7 @@ dependencies = [
|
||||
"pytest>=9.0.2",
|
||||
"pyyaml>=6.0.3",
|
||||
"requests>=2.32.5",
|
||||
"rich>=15.0.0",
|
||||
"sse-starlette>=3.3.4",
|
||||
"tqdm>=4.67.3",
|
||||
"types-pyyaml>=6.0.12.20250915",
|
||||
|
||||
Submodule
+1
Submodule retired/kubernetes_userspace added at 18f62b5fb0
@@ -285,6 +285,7 @@ dependencies = [
|
||||
{ name = "pytest" },
|
||||
{ name = "pyyaml" },
|
||||
{ name = "requests" },
|
||||
{ name = "rich" },
|
||||
{ name = "sse-starlette" },
|
||||
{ name = "tqdm" },
|
||||
{ name = "types-pyyaml" },
|
||||
@@ -307,6 +308,7 @@ requires-dist = [
|
||||
{ name = "pytest", specifier = ">=9.0.2" },
|
||||
{ name = "pyyaml", specifier = ">=6.0.3" },
|
||||
{ name = "requests", specifier = ">=2.32.5" },
|
||||
{ name = "rich", specifier = ">=15.0.0" },
|
||||
{ name = "sse-starlette", specifier = ">=3.3.4" },
|
||||
{ name = "tqdm", specifier = ">=4.67.3" },
|
||||
{ name = "types-pyyaml", specifier = ">=6.0.12.20250915" },
|
||||
@@ -612,6 +614,18 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180, upload-time = "2026-02-09T14:57:25.787Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markdown-it-py"
|
||||
version = "4.2.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "mdurl" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markupsafe"
|
||||
version = "3.0.3"
|
||||
@@ -689,6 +703,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/46/f6b4ad632c67ef35209a66127e4bddc95759649dd595f71f13fba11bdf9a/mcp-1.27.0-py3-none-any.whl", hash = "sha256:5ce1fa81614958e267b21fb2aa34e0aea8e2c6ede60d52aba45fd47246b4d741", size = 215967, upload-time = "2026-04-02T14:48:07.24Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mdurl"
|
||||
version = "0.1.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mergedeep"
|
||||
version = "1.3.4"
|
||||
@@ -1259,6 +1282,19 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481, upload-time = "2023-05-01T04:11:28.427Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rich"
|
||||
version = "15.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "markdown-it-py" },
|
||||
{ name = "pygments" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rpds-py"
|
||||
version = "0.30.0"
|
||||
|
||||
Reference in New Issue
Block a user