mirror of
https://github.com/dredx/prole.git
synced 2026-09-24 15:44:33 +00:00
Compare commits
No commits in common. "9c5c56beb580480dfc66740f1b3d9e9067abe9d2" and "0e937eb9dbb84109e2bd5c416905f154cc8b073c" have entirely different histories.
9c5c56beb5
...
0e937eb9db
1
.gitignore
vendored
1
.gitignore
vendored
@ -106,7 +106,6 @@ deploy/gcp/terraform-setup.txt
|
|||||||
|
|
||||||
# Ansible
|
# Ansible
|
||||||
.ansible/
|
.ansible/
|
||||||
infrastructure/logs/
|
|
||||||
|
|
||||||
# Coverage and testing
|
# Coverage and testing
|
||||||
.coverage
|
.coverage
|
||||||
|
|||||||
36
README.md
36
README.md
@ -1,35 +1,17 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
<a href="https://svc.prole.org"><pre>
|
<a href="https://svc.prole.org"><pre>
|
||||||
# #############################################
|
# ###########################
|
||||||
# ╭───────────────────────────────────────╮ #
|
# ╭──────────────────────╮ #
|
||||||
# │ │ #
|
# │ _ │ #
|
||||||
# │ ___ ____ ___ _ ____ │ #
|
# │ | _/ _ | _ |_ │ #
|
||||||
# │ | _ \| _ \/ _ \| | | ___| │ #
|
# │ |/ | \ | ( |_) │ #
|
||||||
# │ | _/| |_) | | | | | |___ \ │ #
|
# │ │ #
|
||||||
# │ | | | __/| |_| | |___ ___) | │ #
|
# ╰──────────────────────╯ #
|
||||||
# │ |_| |_| \___/|_____|____/ . │ #
|
# ###########################
|
||||||
# │ │ #
|
|
||||||
# │ svc · db · git · api │ #
|
|
||||||
# ╰───────────────────────────────────────╯ #
|
|
||||||
# #############################################
|
|
||||||
</pre></a>
|
</pre></a>
|
||||||
|
|
||||||
[svc.prole.org](https://svc.prole.org) ·
|
|
||||||
[db.prole.org](https://db.prole.org) ·
|
|
||||||
[git.prole.org](https://git.prole.org) ·
|
|
||||||
[api.prole.org](https://api.prole.org)
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
# Prole.
|
# Knoe
|
||||||
|
|
||||||
**Prole** is the reference homelab deployment of the **[Knoe](https://git.knoe.dev)** platform — running on a three-node k3s ARM64 cluster with Samba Active Directory, Kerberos SSO, and Google Workspace login across all services.
|
|
||||||
|
|
||||||
This repo is the `prole.org` fork of [knoe-db](https://git.knoe.dev/knoe-dev/knoe-db), tracking upstream and carrying the cluster-specific manifests, identity configuration, and operational tooling for `prole.org`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Knoe platform
|
|
||||||
|
|
||||||
**Knoe makes it practical to run a Supabase-style platform across air-gapped, edge, and cloud environments.**
|
**Knoe makes it practical to run a Supabase-style platform across air-gapped, edge, and cloud environments.**
|
||||||
|
|
||||||
|
|||||||
@ -196,14 +196,9 @@ spec:
|
|||||||
name: knoe-auth-google-oidc
|
name: knoe-auth-google-oidc
|
||||||
key: hosted_domain
|
key: hosted_domain
|
||||||
optional: true # blank = any Google account; set to restrict to Workspace domain
|
optional: true # blank = any Google account; set to restrict to Workspace domain
|
||||||
# ── prole.org base URL (used for Google OAuth2 redirect_uri) ────────
|
|
||||||
# LoginController.java: @Value("${KNOE_AUTH_BASE_URL:https://api.0.knoe.dev/auth}")
|
|
||||||
# Must be set to the public-facing auth prefix for this cluster.
|
|
||||||
- name: KNOE_AUTH_BASE_URL
|
|
||||||
value: "https://api.prole.org/auth"
|
|
||||||
# ── prole.org Google OAuth2 login (Path A) ───────────────────────
|
# ── prole.org Google OAuth2 login (Path A) ───────────────────────
|
||||||
# Real values: bao/kv/knoe/oauth2/google-prole-org
|
# Real values: bao/kv/knoe/oauth2/google-prole-org
|
||||||
# Secret template: deploy/opentofu/k3s/manifests/knoe/knoe-auth-google-prole-secret.example.yaml
|
# Secret template: deploy/opentofu/k3s/manifests/knoe/knoe-auth-http-keytab-secret.example.yaml
|
||||||
- name: GOOGLE_PROLE_CLIENT_ID
|
- name: GOOGLE_PROLE_CLIENT_ID
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
|
|||||||
@ -11,8 +11,8 @@ metadata:
|
|||||||
# https://console.cloud.google.com/apis/credentials
|
# https://console.cloud.google.com/apis/credentials
|
||||||
# Application type: Web application
|
# Application type: Web application
|
||||||
# Authorized redirect URIs:
|
# Authorized redirect URIs:
|
||||||
# https://api.prole.org/auth/callback/google
|
# https://api.prole.org/auth/auth/callback/google
|
||||||
# https://api.prole.org/auth/enroll/google-callback
|
# https://api.prole.org/auth/auth/enroll/google-callback
|
||||||
#
|
#
|
||||||
# 2. Store real values in OpenBao:
|
# 2. Store real values in OpenBao:
|
||||||
# bao kv put knoe/oauth2/google-prole-org \
|
# bao kv put knoe/oauth2/google-prole-org \
|
||||||
|
|||||||
@ -80,11 +80,7 @@ spec:
|
|||||||
image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.2
|
image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.2
|
||||||
imagePullPolicy: IfNotPresent
|
imagePullPolicy: IfNotPresent
|
||||||
args:
|
args:
|
||||||
# Switched from --provider=google to OIDC via knoe-auth (Phase 2).
|
- --provider=google
|
||||||
# Authentication flow: oauth2-proxy → knoe-auth /authorize → Google → knoe-auth /callback/google → oauth2-proxy /callback
|
|
||||||
# The db-prole-org Google OAuth client (https://db.prole.org/oauth2/callback) is now unused.
|
|
||||||
- --provider=oidc
|
|
||||||
- --oidc-issuer-url=https://api.prole.org/auth
|
|
||||||
- --email-domain=prole.org
|
- --email-domain=prole.org
|
||||||
- --upstream=http://supabase-kong:8000
|
- --upstream=http://supabase-kong:8000
|
||||||
- --http-address=0.0.0.0:4180
|
- --http-address=0.0.0.0:4180
|
||||||
|
|||||||
@ -1,188 +0,0 @@
|
|||||||
# Junie brief: ekosystem UUID system — CNPG wiring and follow-on work
|
|
||||||
|
|
||||||
**Status:** Schema files written, `init_prole_app.sql` wired. CNPG manifests and downstream integrations pending.
|
|
||||||
**Priority:** Medium — work through tasks in order; Tasks 1–3 are independent, Task 4 depends on Task 1.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Background
|
|
||||||
|
|
||||||
A stable routable base36 UUID system has been built for ekosystem (tenant-based federated DB cluster) objects. Two schema files land in `knoe-db/schema/`:
|
|
||||||
|
|
||||||
| File | Contents |
|
|
||||||
|---|---|
|
|
||||||
| `schema/ekosystem.sql` | Core: `ekosystem_id()`, `ekosystem_decode()`, `ekosystem_tenant()`, `register_tenant()`, `project_prefix()`, `knoe.tenants`, `knoe.ekosystem_seq` |
|
|
||||||
| `schema/ekosystem_objects.sql` | Objects: `ekosystem_object()`, `ekosystem_kind()`, `object_kinds`, `cross_grants`, `grant_object()`, `revoke_object()`, `can_access()`, `my_grants()` |
|
|
||||||
|
|
||||||
**Naming conventions in use:**
|
|
||||||
|
|
||||||
```
|
|
||||||
p_{16-char-uuid} — project namespace / schema root
|
|
||||||
u_{16-char-uuid} — service account / PG role
|
|
||||||
e_{16-char-uuid} — embedding store
|
|
||||||
t_{16-char-uuid}_{name} — data table (prefix 19 chars, PG budget: 44 for name)
|
|
||||||
x_{16-char-uuid}_{name} — index
|
|
||||||
```
|
|
||||||
|
|
||||||
`init_prole_app.sql` already wires these for direct `psql -f` execution. The CNPG production path (which uses inline `postInitSQL` YAML arrays) needs separate wiring — see Task 1.
|
|
||||||
|
|
||||||
**Tenant model:**
|
|
||||||
- `knoe-db` = tenant_id 0, hardcoded root authority
|
|
||||||
- All other ekosystems (including `prole`) receive a UUID minted by the root and an assigned integer `tenant_id`
|
|
||||||
- Run `SELECT * FROM knoe.register_tenant('prole')` once on first deploy and record the output
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Task 1 — Wire schema into all three CNPG manifests via ConfigMap
|
|
||||||
|
|
||||||
**Why:** CNPG `postInitSQL` is a YAML string array — it cannot reference external files. The functions use PL/pgSQL with dollar-quoting, which is unreadable and fragile when inlined in YAML. The correct CNPG mechanism is a Kubernetes ConfigMap referenced via `postInitApplicationSQLRefs`.
|
|
||||||
|
|
||||||
**Files to update:**
|
|
||||||
- `k8s/knoe/knoe-db.yaml` (k3s production)
|
|
||||||
- `deploy/gcp/gke/knoe-db.yaml` (GKE)
|
|
||||||
- `deploy/opentofu/k3s/manifests/knoe/knoe-db.yaml` (OpenTofu k3s)
|
|
||||||
|
|
||||||
### Step 1a — Create the ConfigMap manifest
|
|
||||||
|
|
||||||
Create `k8s/knoe/knoe-ekosystem-sql.yaml`:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: knoe-ekosystem-sql
|
|
||||||
namespace: knoe-db
|
|
||||||
data:
|
|
||||||
ekosystem.sql: |
|
|
||||||
<contents of schema/ekosystem.sql verbatim>
|
|
||||||
ekosystem_objects.sql: |
|
|
||||||
<contents of schema/ekosystem_objects.sql verbatim>
|
|
||||||
```
|
|
||||||
|
|
||||||
Use a Makefile target or a generation script to keep the ConfigMap content in sync with the source SQL files rather than hand-editing. Suggested target: `make k8s/knoe/knoe-ekosystem-sql.yaml`.
|
|
||||||
|
|
||||||
### Step 1b — Reference from CNPG cluster manifests
|
|
||||||
|
|
||||||
In each CNPG manifest, append to the `bootstrap.initdb` section **after** the existing `postInitSQL` block. The ekosystem SQL must run after extensions and role setup, so order matters:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
postInitApplicationSQLRefs:
|
|
||||||
configMapRefs:
|
|
||||||
- name: knoe-ekosystem-sql
|
|
||||||
key: ekosystem.sql
|
|
||||||
- name: knoe-ekosystem-sql
|
|
||||||
key: ekosystem_objects.sql
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** `postInitApplicationSQLRefs` was added in CNPG v1.20. Verify against the running operator version before applying:
|
|
||||||
```bash
|
|
||||||
kubectl -n cnpg-system get deployment cnpg-controller-manager \
|
|
||||||
-o jsonpath='{.spec.template.spec.containers[0].image}'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 1c — Apply and verify
|
|
||||||
|
|
||||||
```bash
|
|
||||||
kubectl apply -f k8s/knoe/knoe-ekosystem-sql.yaml
|
|
||||||
|
|
||||||
# Verify functions exist after next cluster bootstrap or via psql:
|
|
||||||
psql "host=pg.prole.org ..." -c "\df knoe.ekosystem_*"
|
|
||||||
psql "host=pg.prole.org ..." -c "SELECT * FROM knoe.ekosystem_decode(knoe.ekosystem_id(0,0));"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Task 2 — Register `prole` as tenant 1
|
|
||||||
|
|
||||||
**Why:** The `knoe.tenants` table currently only has the root row (tenant_id=0, knoe-db). Prole needs to be registered so its project UUIDs embed the correct `tenant_id=1` in their bits.
|
|
||||||
|
|
||||||
**Steps:**
|
|
||||||
1. Connect to the production DB via `pg.prole.org`
|
|
||||||
2. Run: `SELECT * FROM knoe.register_tenant('prole');`
|
|
||||||
3. Record the returned `uuid` — this is prole's stable external identifier:
|
|
||||||
- DNS: `db.{uuid}.prole.org`
|
|
||||||
- Shown in service-account prefixes: `u_{uuid}`
|
|
||||||
4. Save the UUID in `conf/` or a new `docs/ekosystem-tenants.md` for reference
|
|
||||||
5. When generating prole project IDs, use `tenant_id=1`:
|
|
||||||
`SELECT knoe.ekosystem_id(1, 0);`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Task 3 — Python counterpart utility
|
|
||||||
|
|
||||||
**Why:** ClickHouse and SQLite nodes cannot call PostgreSQL functions. Application-layer code (Python) needs to mint ekosystem IDs without a PG connection.
|
|
||||||
|
|
||||||
**Location:** `src/knoe/ekosystem.py` (or `lib/ekosystem.py` depending on project layout)
|
|
||||||
|
|
||||||
**Spec:**
|
|
||||||
|
|
||||||
```python
|
|
||||||
EPOCH_MS = 1767225600000 # 2026-01-01 00:00:00 UTC
|
|
||||||
ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz'
|
|
||||||
|
|
||||||
# Bit layout: [49:ts_ms | 12:tenant | 10:shard | 11:seq]
|
|
||||||
# Must produce identical output to knoe.ekosystem_id() in PostgreSQL.
|
|
||||||
|
|
||||||
class EkosystemID:
|
|
||||||
def __init__(self, tenant_id: int, shard_id: int): ...
|
|
||||||
def generate(self) -> str: ... # thread-safe, per-ms atomic seq
|
|
||||||
def project_prefix(self) -> str: ... # 'p_{uuid}_'
|
|
||||||
def object_name(self, kind: str) -> str: # '{prefix}_{uuid}'
|
|
||||||
|
|
||||||
def decode(uid: str) -> dict: ... # tenant_id, shard_id, ts_ms, seq, issued_at
|
|
||||||
def can_access(uid: str, kind: str, tenant_id: int, grants: list) -> bool: ...
|
|
||||||
```
|
|
||||||
|
|
||||||
The per-ms atomic counter (`seq`) should use `threading.Lock()` — same Snowflake pattern as the SQL version. No global database locking.
|
|
||||||
|
|
||||||
Include a round-trip test: `assert decode(EkosystemID(1, 0).generate())['tenant_id'] == 1`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Task 4 — Align `knoe.user` with ekosystem user UUIDs
|
|
||||||
|
|
||||||
**Why:** `knoe.user` (in the CNPG manifests) uses `SERIAL` integer IDs. Users created via the ekosystem system get `u_{uuid}` PG role names. These two identity systems need a join column so that a Kerberos/LDAP principal can be resolved to both their `knoe.user.id` (for existing app code) and their ekosystem UUID (for routing and LDAP group membership in cross-grants).
|
|
||||||
|
|
||||||
**Steps:**
|
|
||||||
1. Add `ekosystem_uuid text UNIQUE` column to `knoe.user`:
|
|
||||||
```sql
|
|
||||||
ALTER TABLE knoe.user ADD COLUMN IF NOT EXISTS ekosystem_uuid text UNIQUE;
|
|
||||||
```
|
|
||||||
2. When a new user is provisioned, call `knoe.ekosystem_id(tenant_id, shard_id)` to mint their UUID and write it to `ekosystem_uuid`
|
|
||||||
3. Add a migration entry in the CNPG manifests' `postInitSQL` for the new column (or apply directly to the live DB and track in `docs/TODO.md`)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Task 5 — LDAP/Samba AD: wire `cross_grants` to AD group membership
|
|
||||||
|
|
||||||
**Why:** `knoe.cross_grants` is the policy source of truth for cross-ekosystem sharing. The Samba AD domain controller (`myrddin.prole.org`) enforces access at the auth layer. These need to be kept in sync.
|
|
||||||
|
|
||||||
**Spec (design only — implementation requires knoe-auth work to be further along):**
|
|
||||||
- A periodic reconciler reads `knoe.my_grants(tenant_id)` for each registered tenant
|
|
||||||
- For each active `received` grant, it ensures the grantee's AD user/group is a member of the AD group corresponding to the grantor's project
|
|
||||||
- AD group naming convention: `ekosystem-{object_uuid}` (stays under 64-char SAM-Account-Name limit at 10+16=26 chars)
|
|
||||||
- Revoking a grant removes the AD group membership
|
|
||||||
|
|
||||||
**Prerequisite:** `pg_oauth` / `pg-knoe-auth` OIDC path must be further along so the reconciler can authenticate to PG using a service Kerberos principal.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Acceptance criteria (all tasks)
|
|
||||||
|
|
||||||
```sql
|
|
||||||
-- Task 1: functions exist in production
|
|
||||||
SELECT routine_name FROM information_schema.routines
|
|
||||||
WHERE routine_schema = 'knoe' AND routine_name LIKE 'ekosystem%';
|
|
||||||
|
|
||||||
-- Task 2: prole registered
|
|
||||||
SELECT * FROM knoe.tenants;
|
|
||||||
-- expect: (0, NULL, 'knoe-db'), (1, '{16-char-uuid}', 'prole')
|
|
||||||
|
|
||||||
-- Task 3: Python output matches SQL output for same inputs
|
|
||||||
-- (unit test in test suite)
|
|
||||||
|
|
||||||
-- Task 4: knoe.user has ekosystem_uuid column
|
|
||||||
\d knoe.user
|
|
||||||
|
|
||||||
-- Task 5: design doc only for now
|
|
||||||
```
|
|
||||||
Loading…
Reference in New Issue
Block a user