various updates

This commit is contained in:
2026-09-07 11:37:56 -04:00
parent 9995fb29ff
commit fc91437228
81 changed files with 1478 additions and 1437 deletions
+19 -9
View File
@@ -42,6 +42,7 @@ or give me access to your servers.
- [bash](#bash) - [bash](#bash)
- [Bulk File/Folder Renaming](#bulk-filefolder-renaming) - [Bulk File/Folder Renaming](#bulk-filefolder-renaming)
- [Escaping a Stuck SSH Terminal](#escaping-a-stuck-ssh-terminal) - [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) - [SSH Setup](#ssh-setup)
- [Git GPG Commit Signing](#git-gpg-commit-signing) - [Git GPG Commit Signing](#git-gpg-commit-signing)
- [Important Dates and Times](#important-dates-and-times) - [Important Dates and Times](#important-dates-and-times)
@@ -133,6 +134,14 @@ done
Press the following keys: enter + ~ + . Press the following keys: enter + ~ + .
### Curl output hidden by bash prompt
Add this to your ~/.curlrc
```conf
-w "\n"
```
## SSH Setup ## SSH Setup
Generate a key (password protect it!) 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 ## Important Dates and Times
| Time | Day | Description | | Time | Day | Description |
| ----- | --- | ---------------------------------- | | ----- | --- | ------------------------------ |
| 00:00 | All | Automated builds | | 00:00 | All | Automated builds |
| 00:00 | All | NAS Snapshots | | 00:00 | All | NAS Snapshots |
| 02:00 | All | Backups | | 01:00 | All | Backups |
| 04:00 | All | Bare Metal Server Security Updates | | 03:00 | All | App Updates |
| 05:00 | All | VM Server Security Updates | | 04:00 | All | Bare Metal Server Updates |
| 05:00 | All | Unifi Protect Firmware Updates | | 05:00 | All | VM Server Updates |
| 06:00 | All | Unifi Network Firmware Updates | | 05:00 | All | Unifi Protect Firmware Updates |
| 06:00 | All | Unifi Network Firmware Updates |
## Project Lifecycle ## Project Lifecycle
+14
View File
@@ -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
+1 -1
View File
@@ -4,7 +4,7 @@ networks:
services: services:
server: server:
image: docker.gitea.com/gitea:1.25.3 image: docker.gitea.com/gitea:1.26.1
container_name: gitea container_name: gitea
environment: environment:
- USER_UID=1001 - 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
+1 -1
View File
@@ -10,7 +10,7 @@ DB_DATA_LOCATION=/srv/immich-db/postgres
# TZ=Etc/UTC # TZ=Etc/UTC
# The Immich version to use. You can pin this to a specific version like "v2.1.0" # 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 # 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 # 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"
+5 -181
View File
@@ -1,186 +1,10 @@
# Podman keycloak # Podman keycloak
- [Podman keycloak](#podman-keycloak) - [Podman keycloak](#podman-keycloak)
- [Setup keycloak Project](#setup-keycloak-project) - [Adding a Client](#adding-a-client)
- [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)
## Setup keycloak Project ## Adding a Client
- [ ] Copy and rename this folder to active/container_keycloak 1. Create a new client
- [ ] Find and replace keycloak with the name of the service. 2. Clients -> click on client -> Client scopes -> client-frontend-dedicated -> Scope -> turn off Full Scope Allowed
- [ ] Create the rootless user to run the podman containers 3.
- [ ] 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,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
+167
View File
@@ -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 HealthRetries=3
HealthStartPeriod=40s HealthStartPeriod=40s
HealthTimeout=10s 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 Volume=/home/ai/litellm-config.yaml:/app/config.yaml:z
Exec=--config=/app/config.yaml Exec=--config=/app/config.yaml
+24
View File
@@ -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
```
+1
View File
@@ -0,0 +1 @@
# base-url:
Binary file not shown.
Binary file not shown.
+16
View File
@@ -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: esphome:
name: tab2 name: dickhead
friendly_name: M5Stack Tab5 2 friendly_name: Dickhead
on_boot: on_boot:
# Set the charing icon to the correct state on boot # Set the charging icon to the correct state on boot
- then: - then:
- logger.log: "Delaying backlight initialization"
- delay: 2s
- logger.log: "End delay"
- if: - if:
condition: condition:
lambda: return id(charging).state; lambda: return id(charging).state;
@@ -17,6 +20,7 @@ esphome:
esp32: esp32:
board: esp32-p4-evboard board: esp32-p4-evboard
flash_size: 16MB flash_size: 16MB
cpu_frequency: 360MHz
framework: framework:
type: esp-idf type: esp-idf
advanced: advanced:
@@ -36,6 +40,7 @@ esp32_hosted:
logger: logger:
hardware_uart: USB_SERIAL_JTAG hardware_uart: USB_SERIAL_JTAG
level: DEBUG
psram: psram:
mode: hex mode: hex
@@ -53,8 +58,8 @@ ota:
platform: esphome platform: esphome
wifi: wifi:
ssid: !secret wifi_ssid ssid: !secret mobile_wifi_ssid
password: !secret wifi_password password: !secret mobile_wifi_pass
fast_connect: true fast_connect: true
on_connect: on_connect:
- lvgl.label.update: - lvgl.label.update:
@@ -63,15 +68,50 @@ wifi:
- select.set: - select.set:
id: dac_output id: dac_output
option: "LINE1" 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: on_disconnect:
- lvgl.label.update: - lvgl.label.update:
id: lbl_status id: lbl_status
text: "DISCONNECTED" text: "DISCONNECTED"
# ap: ap:
# password: !secret hotspot_password password: !secret hotspot_password
# ap_timeout: 90s 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: time:
- platform: sntp - platform: sntp
@@ -82,14 +122,13 @@ time:
- 1.pool.ntp.org - 1.pool.ntp.org
- 2.pool.ntp.org - 2.pool.ntp.org
# wireguard: wireguard:
# address: !secret tab1_wg_ip address: !secret tab1_wg_ip
# private_key: !secret tab1_wg_pk private_key: !secret tab1_wg_pk
# peer_endpoint: !secret wg_host peer_endpoint: !secret wg_host
# peer_public_key: !secret wg_pubkey peer_public_key: !secret wg_pubkey
netmask: 0.0.0.0
# # Optional keepalive (disabled by default) peer_persistent_keepalive: 25s
# peer_persistent_keepalive: 25s
i2c: i2c:
- id: bsp_bus - id: bsp_bus
@@ -196,6 +235,39 @@ binary_sensor:
pin: pin:
pi4ioe5v6408: pi4ioe1 pi4ioe5v6408: pi4ioe1
number: 7 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: sensor:
- platform: ina226 - platform: ina226
@@ -255,8 +327,8 @@ touchscreen:
- logger.log: "LVGL resuming" - logger.log: "LVGL resuming"
- lvgl.resume: - lvgl.resume:
- light.turn_on: backlight - light.turn_on: backlight
on_release: # on_release:
- media_player.stop: # - media_player.stop:
esp_ldo: esp_ldo:
- voltage: 2.5V - voltage: 2.5V
@@ -287,7 +359,7 @@ light:
restore_mode: ALWAYS_ON restore_mode: ALWAYS_ON
default_transition_length: 250ms default_transition_length: 250ms
initial_state: initial_state:
brightness: "100%" brightness: "50%"
image: image:
defaults: defaults:
@@ -318,20 +390,41 @@ lvgl:
transition_length: 15s transition_length: 15s
- lvgl.pause: - lvgl.pause:
widgets: 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: - image:
id: listen_icon_widget id: listen_icon_widget
src: va_idle src: va_idle
align: CENTER align: CENTER
- label:
align: TOP_MID
id: lbl_status
text_font: montserrat_48
text: "CONNECTING..."
- label: - label:
align: BOTTOM_LEFT align: BOTTOM_LEFT
id: lbl_version id: lbl_version
text_font: montserrat_12 text_font: montserrat_12
text: "v0.5" text: "v0.6"
- label: - label:
align: BOTTOM_RIGHT align: BOTTOM_RIGHT
id: lbl_battery id: lbl_battery
@@ -341,21 +434,38 @@ lvgl:
id: charging_icon_widget id: charging_icon_widget
src: charging_icon src: charging_icon
align: TOP_RIGHT align: TOP_RIGHT
- slider: - button:
id: backlight_slider id: volume_up_widget
widgets:
- label:
text: "\uF028"
text_font: montserrat_48
text_align: CENTER
align: CENTER
x: 20 x: 20
y: 50 y: 20
width: 30 width: 100
height: 220 height: 100
pad_all: 8 pad_all: 8
min_value: 0 - button:
max_value: 255 id: volume_down_widget
on_release: widgets:
- homeassistant.action: - label:
action: light.turn_on text: "\uF027"
data: text_font: montserrat_48
entity_id: light.backlight text_align: CENTER
brightness: !lambda return int(x); 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 # The DAC Output select needs to be manually (or with an automation) changed to `LINE1` for the onboard speaker
select: select:
@@ -414,7 +524,7 @@ speaker:
audio_dac: es8388_dac audio_dac: es8388_dac
dac_type: external dac_type: external
channel: mono channel: mono
buffer_duration: 100ms buffer_duration: 60000ms
bits_per_sample: 16bit bits_per_sample: 16bit
sample_rate: 48000 sample_rate: 48000
@@ -429,9 +539,12 @@ media_player:
micro_wake_word: micro_wake_word:
id: mww id: mww
models: models:
- okay_nabu - model:
- hey_mycroft type: local
- hey_jarvis path: alexa.json
id: alexa
probability_cutoff: 0.97
sliding_window_size: 5
on_wake_word_detected: on_wake_word_detected:
- voice_assistant.start: - voice_assistant.start:
wake_word: !lambda return wake_word; wake_word: !lambda return wake_word;
+7
View File
@@ -56,6 +56,13 @@ Make sure your permissions are set correctly
sudo usermod -a -G dialout ducoterra 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 Then "run" your config file
```bash ```bash
+16
View File
@@ -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 active/device_home_assistant/config added at 5a3ce1da52
+48
View File
@@ -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"
```
+89
View File
@@ -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
View File
@@ -1,114 +1,204 @@
# Fedora Kinoite # Fedora Kinoite
- [Fedora Kinoite](#fedora-kinoite) - [Fedora Kinoite](#fedora-kinoite)
- [TPM2 Luks Decryption](#tpm2-luks-decryption) - [Adding users to groups (usermod replacement)](#adding-users-to-groups-usermod-replacement)
- [Podman](#podman) - [System Prereqs](#system-prereqs)
- [Docker Compose and Docker Buildkit with Rootless Podman](#docker-compose-and-docker-buildkit-with-rootless-podman) - [Toolbox](#toolbox)
- [rpm-ostree](#rpm-ostree) - [Clipboard Sharing](#clipboard-sharing)
- [Git, Vim, etc](#git-vim-etc) - [Running Host Commands](#running-host-commands)
- [Libvirt, Qemu, KVM](#libvirt-qemu-kvm) - [Saving a Toolbox](#saving-a-toolbox)
- [ROCM](#rocm) - [Apps](#apps)
- [Ollama](#ollama) - [Gear Lever](#gear-lever)
- [Network](#network) - [VSCodium](#vscodium)
- [Hostname](#hostname) - [Ansible](#ansible)
- [VLAN Setup with nmcli](#vlan-setup-with-nmcli) - [OpenCode](#opencode)
- [GPU Support in Distrobox](#gpu-support-in-distrobox) - [Nextcloud](#nextcloud)
- [Automatic Display Switching](#automatic-display-switching) - [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 ## Adding users to groups (usermod replacement)
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:
```bash ```bash
# Show tpm2 devices grep -E '^weldr:' /usr/lib/group | sudo tee -a /etc/group
systemd-cryptenroll --tpm2-device=list sudo usermod -aG weldr $USER
# 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
``` ```
When you update you'll need to reenroll. Add this to your ~/.bashrc ## System Prereqs
```bash ```bash
# LUKS TPM2 commands # Appimage support
alias tpm2-reenroll='sudo systemd-cryptenroll /dev/nvme0n1p3 --wipe-slot=tpm2 --tpm2-device=auto --tpm2-pcrs=0,2,4,7,8,9' 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 ### Clipboard Sharing
the podman auth file to somewhere persistent, otherwise it'll get deleted every time you
reboot.
Add this to your `.bashrc`: Inside the toolbox run:
```bash ```bash
# Podman auth file sudo dnf install -y wl-clipboard
export REGISTRY_AUTH_FILE=$HOME/.podman-auth.json
``` ```
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. I create a `toolbox.sh` in `~/.bashrc.d` with functions that map to host
commands like so:
<https://emersion.fr/blog/2025/using-podman-compose-and-buildkit/>
```bash ```bash
rpm-ostree install docker-compose docker-buildx # Use flatpak-spawn --host podman inside toolbox, regular podman outside
reboot podman() {
systemctl --user enable --now podman.socket if [ -f /run/.containerenv ]; then
docker context create podman --docker host=unix://$XDG_RUNTIME_DIR/podman/podman.sock flatpak-spawn --host podman "$@"
docker context use podman else
command podman "$@"
fi
}
### Autostarting services with quadlets # Notify the user if they're trying to run a command that doesn't work in a toolbox
rpm-ostree() {
If you want to run something as your user at boot (like a systemd process, think ollama) you can if [ -f /run/.containerenv ]; then
create a user quadlets like so: echo "Command must be run outside toolbox."
else
```bash command rpm-ostree "$@"
# Generate the .container file fi
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
``` ```
## 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 Note: if you don't need them, remove extra repos
@@ -133,169 +223,6 @@ rpm-ostree apply-live
rpm-ostree status rpm-ostree status
``` ```
### Git, Vim, etc ## Accent keys when holding a key
Some packages are nice to have at the system level. Settings -> keyboard -> virtual keyboard -> disable virtual keyboard
```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
```
+38
View File
@@ -4,6 +4,7 @@
- [Installation](#installation) - [Installation](#installation)
- [Setup SSH](#setup-ssh) - [Setup SSH](#setup-ssh)
- [DNF](#dnf) - [DNF](#dnf)
- [Version lock](#version-lock)
- [Power Profiles with Tuned](#power-profiles-with-tuned) - [Power Profiles with Tuned](#power-profiles-with-tuned)
- [Fail2Ban](#fail2ban) - [Fail2Ban](#fail2ban)
- [BTRFS Parent Volumes](#btrfs-parent-volumes) - [BTRFS Parent Volumes](#btrfs-parent-volumes)
@@ -84,6 +85,7 @@
- [Sound Devices](#sound-devices) - [Sound Devices](#sound-devices)
- [Install ffmpegthumbnailer, remove totem](#install-ffmpegthumbnailer-remove-totem) - [Install ffmpegthumbnailer, remove totem](#install-ffmpegthumbnailer-remove-totem)
- [Add compatibility for HEIC to mogrify](#add-compatibility-for-heic-to-mogrify) - [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> <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 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 ## Power Profiles with Tuned
1. `dnf install tuned` 1. `dnf install tuned`
@@ -1486,3 +1498,29 @@ sudo dnf install ffmpegthumbnailer
```bash ```bash
sudo dnf install libheic-freeworld 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
-14
View File
@@ -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
+32 -151
View File
@@ -3,10 +3,6 @@
- [Self Hosted AI Stack](#self-hosted-ai-stack) - [Self Hosted AI Stack](#self-hosted-ai-stack)
- [Notes](#notes) - [Notes](#notes)
- [Podman Volume Locations](#podman-volume-locations) - [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) - [Setup](#setup)
- [Create the AI user](#create-the-ai-user) - [Create the AI user](#create-the-ai-user)
- [Helper aliases](#helper-aliases) - [Helper aliases](#helper-aliases)
@@ -36,16 +32,7 @@
- [open-webui](#open-webui) - [open-webui](#open-webui)
- [Upgrade Open Webui](#upgrade-open-webui) - [Upgrade Open Webui](#upgrade-open-webui)
- [lite-llm](#lite-llm) - [lite-llm](#lite-llm)
- [Install Services with Quadlets](#install-services-with-quadlets) - [Install the update script](#install-the-update-script)
- [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)
- [Benchmark Results](#benchmark-results) - [Benchmark Results](#benchmark-results)
- [Testing with Curl](#testing-with-curl) - [Testing with Curl](#testing-with-curl)
- [OpenAI API](#openai-api) - [OpenAI API](#openai-api)
@@ -77,33 +64,6 @@ hf-download ()
`~/.local/share/containers/storage/volumes/` `~/.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 ## Setup
### Create the AI user ### 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 In my completely subjective opinion: 5 bit quant is usually the sweet spot for
unsloth models. Q5_K_S is usually just fine. unsloth models. Q5_K_S is usually just fine.
I usually download the F16 mmproj files. This is also completely subjective. I usually download the BF16 mmproj files. This is also completely subjective.
BF16 is fine. F32 is overkill.
#### Text models #### Text models
@@ -391,20 +350,43 @@ podman run \
--name llama-server-demo \ --name llama-server-demo \
--device=/dev/kfd \ --device=/dev/kfd \
--device=/dev/dri \ --device=/dev/dri \
-v /home/ai/models/text:/models:z \ -v $(pwd):/models:z \
-p 8010:8000 \ -p 8010:8000 \
--ipc host \ --ipc host \
localhost/llama-cpp-vulkan:latest \ localhost/llama-cpp-vulkan:latest \
--host 0.0.0.0 \ --host 0.0.0.0 \
--port 8000 \ --port 8000 \
-c 128000 \
--perf \ --perf \
--n-gpu-layers all \ --n-gpu-layers all \
--jinja \ --jinja \
--models-max 1 \ --model /models/gemma-4-E4B-it-qat-UD-Q4_K_XL.gguf \
--models-dir /models \ --mmproj /models/mmproj-F16.gguf \
--chat-template-kwargs '{"enable_thinking": false}' \ --temp 1.0 \
-m /models/qwen3.5-35b-a3b --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 Embedding models
@@ -595,96 +577,7 @@ podman run \
-p 4000:4000 -p 4000:4000
``` ```
## Install Services with Quadlets ## Install the update script
### 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
```bash ```bash
# 1. Builds the latest llama.cpp and stable-diffusion.cpp # 1. Builds the latest llama.cpp and stable-diffusion.cpp
@@ -696,18 +589,6 @@ chmod +x update-script.sh
./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 ## 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) 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
+5
View File
@@ -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
"""
+2
View File
@@ -29,6 +29,8 @@ dnf install -y osbuild-composer composer-cli
systemctl enable --now osbuild-composer.socket systemctl enable --now osbuild-composer.socket
# Optional: add your user to the weldr group # 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 sudo usermod -aG weldr $USER
# Optional: cockpit dependency # Optional: cockpit dependency
+48
View File
@@ -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
```
+8 -2
View File
@@ -30,11 +30,17 @@ sudo smbpasswd -a sambauser
## Create a SMB Share ## Create a SMB Share
```bash ```bash
# Add the share user
sudo useradd -r -M -s /usr/sbin/nologin sambauser
sudo smbpasswd -a sambauser
# Create share # 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 # 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 # Run restorecon at the root of the btrfs subvolume
sudo restorecon -FRv /srv sudo restorecon -FRv /srv
+33
View File
@@ -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
+7
View File
@@ -0,0 +1,7 @@
# Toolbox
## How to Use
```bash
podman build -t localhost/fedora-toolbox:44 active/software_toolbx
```
+9
View File
@@ -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
-1
View File
@@ -40,7 +40,6 @@ Virtual Machine Management
<https://libvirt.org/uri.html#ssh-transport> <https://libvirt.org/uri.html#ssh-transport>
### Configuring Aliases ### Configuring Aliases
1. Edit `~/.config/libvirt/libvirt.conf` 1. Edit `~/.config/libvirt/libvirt.conf`
2. Add your aliases 2. Add your aliases
+20 -33
View File
@@ -1,24 +1,19 @@
import json import json
import os import os
import smtplib import smtplib
import ssl
import subprocess import subprocess
from email.message import EmailMessage from email.message import EmailMessage
from pathlib import Path from pathlib import Path
from typing import Iterable, TypedDict, cast from typing import Iterable, TypedDict, cast
import requests
from dotenv import dotenv_values, load_dotenv from dotenv import dotenv_values, load_dotenv
from openai import OpenAI from openai import OpenAI
from openai.types.chat import ChatCompletionMessageParam, ChatCompletionToolUnionParam from openai.types.chat import ChatCompletionMessageParam, ChatCompletionToolUnionParam
class AWS_SES_DOTENV(TypedDict): class NTFY_DOTENV(TypedDict):
USER: str TOKEN: str
PASSWORD: str
ENDPOINT: str
TLS_PORT: str
SENDER: str
RECEIVER: str
class ToolCallController: class ToolCallController:
@@ -209,39 +204,30 @@ def smartctl(device_path: str) -> str:
return output 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") ses_dotenv_location = Path(os.getenv("HOME", "/root"), ".env/aws_ses")
print(f"Loading env from {ses_dotenv_location}") print(f"Loading env from {ses_dotenv_location}")
raw_values = dotenv_values(ses_dotenv_location) raw_values = dotenv_values(ses_dotenv_location)
if raw_values: 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}") # print(f"AWS SES Credentials loaded: {aws_ses_config}")
return aws_ses_config return ntfy_config
print("No email credentials supplied. Exiting.") print("No ntfy credentials supplied. Exiting.")
exit(1) exit(1)
def alert_user(message: str) -> str: def alert_user(message: str) -> str:
ses_config = load_ses_creds() ntfy_creds = load_ntfy_creds()
port = int(ses_config["TLS_PORT"])
user = ses_config["USER"]
password = ses_config["PASSWORD"]
sender = ses_config["SENDER"]
receiver = ses_config["RECEIVER"]
# Create a secure SSL context requests.post(
context = ssl.create_default_context() "https://ntfy.reeseapps.com/servers",
data=message,
msg = EmailMessage() headers={
msg["Subject"] = "Agent Disk Report" "Authorization": f"Bearer {ntfy_creds['TOKEN']}",
msg["From"] = sender "Title": "Agent Disk Report",
msg["To"] = receiver "Priority": "default",
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)
return "Sent." return "Sent."
@@ -322,13 +308,14 @@ def run_conversation(user_message: str, max_tool_calls=10):
if __name__ == "__main__": if __name__ == "__main__":
load_dotenv() load_dotenv()
api_key = os.getenv("OPENAI_API_KEY", "") 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 # Example usage
print( print(
run_conversation( run_conversation(
"Check the btrfs pools on this system. Take the appropriate action if any pools aren't " "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 " "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, " "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."
) )
) )
-5
View File
@@ -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
View File
-1
View File
@@ -1 +0,0 @@
3.13
-9
View File
@@ -1,9 +0,0 @@
# Update
Updates every server in the fleet
## Usage
```bash
uv run automations/updates/main.py
```
-64
View File
@@ -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()
-12
View File
@@ -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",
]
-121
View File
@@ -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" },
]
+1
View File
@@ -16,6 +16,7 @@ dependencies = [
"pytest>=9.0.2", "pytest>=9.0.2",
"pyyaml>=6.0.3", "pyyaml>=6.0.3",
"requests>=2.32.5", "requests>=2.32.5",
"rich>=15.0.0",
"sse-starlette>=3.3.4", "sse-starlette>=3.3.4",
"tqdm>=4.67.3", "tqdm>=4.67.3",
"types-pyyaml>=6.0.12.20250915", "types-pyyaml>=6.0.12.20250915",
Submodule retired/kubernetes_userspace added at 18f62b5fb0
Generated
+36
View File
@@ -285,6 +285,7 @@ dependencies = [
{ name = "pytest" }, { name = "pytest" },
{ name = "pyyaml" }, { name = "pyyaml" },
{ name = "requests" }, { name = "requests" },
{ name = "rich" },
{ name = "sse-starlette" }, { name = "sse-starlette" },
{ name = "tqdm" }, { name = "tqdm" },
{ name = "types-pyyaml" }, { name = "types-pyyaml" },
@@ -307,6 +308,7 @@ requires-dist = [
{ name = "pytest", specifier = ">=9.0.2" }, { name = "pytest", specifier = ">=9.0.2" },
{ name = "pyyaml", specifier = ">=6.0.3" }, { name = "pyyaml", specifier = ">=6.0.3" },
{ name = "requests", specifier = ">=2.32.5" }, { name = "requests", specifier = ">=2.32.5" },
{ name = "rich", specifier = ">=15.0.0" },
{ name = "sse-starlette", specifier = ">=3.3.4" }, { name = "sse-starlette", specifier = ">=3.3.4" },
{ name = "tqdm", specifier = ">=4.67.3" }, { name = "tqdm", specifier = ">=4.67.3" },
{ name = "types-pyyaml", specifier = ">=6.0.12.20250915" }, { 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" }, { 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]] [[package]]
name = "markupsafe" name = "markupsafe"
version = "3.0.3" 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" }, { 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]] [[package]]
name = "mergedeep" name = "mergedeep"
version = "1.3.4" 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" }, { 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]] [[package]]
name = "rpds-py" name = "rpds-py"
version = "0.30.0" version = "0.30.0"