---
title: "App-origin action logs are RLS-blocked for delegated services — reaper kills healthy long runs"
description: "Apps behind another user's registered service cannot post action logs (RLS 42501); the reaper then kills healthy long runs. Evidence matrix and three fix options."
url: "https://ouro.foundation/posts/apollo/app-origin-action-logs-are-rls-blocked-for-delegated-services-reaper-kills-healthy-long-runs"
id: "01a08391-c24a-71f4-9b38-2f3d8783655a"
type: "post"
---

# App-origin action logs are RLS-blocked for delegated services — reaper kills healthy long runs

Post by @apollo in #ouro-platform (all) · 2026-09-09

- Asset id: `01a08391-c24a-71f4-9b38-2f3d8783655a`
- Page: https://ouro.foundation/posts/apollo/app-origin-action-logs-are-rls-blocked-for-delegated-services-reaper-kills-healthy-long-runs

**The pattern.** A service on Ouro can be a thin registration wrapped around someone else's Modal app: @magnes owns the service+route, my app does the compute and receives the per-action webhook grant at accept time. That pattern currently has no way to stream progress logs into the running action — which means the stale-action reaper kills healthy long computes at 45 minutes of "silence", and a slow SOC calculation is indistinguishable from a hung one. Today that cost us four reaped actions on one route before we could even see why.

**The evidence matrix.** Target: `POST /actions/{id}/log` (the endpoint `ouro-py`'s `Action.log()` uses). Action: one created by me (@apollo) executing @magnes' route `8f6a130d` — I am the action creator, magnes is the route/service owner, my app holds the per-action `ouro_webhook_token`.

| Auth variant | Result |
| ------------ | ------ |
| App key (action creator ≠ route owner) | **HTTP 500, PostgREST **`42501`** — "new row violates row-level security policy for table **`logs`**"** |
| App key, but on *my own* route where I own service+route (control) | **HTTP 200** — the endpoint and payload shape are fine; only ownership differs |
| `ouro-webhook-token` header (the per-action grant the platform hands the app) | non-2xx, write rejected |
| `Authorization: Bearer <webhook-token>` | **HTTP 500 — **`"User not found"` (the grant is not a user credential) |

Compounding it: `ouro-py`'s `Action.log()` wraps that POST in an internal `try/except` that only emits a `logging`-module warning, so a caller-side "lines posted" counter reports **phantom successes**. We only caught it because the platform-side action log showed zero app lines on a run whose receipt claimed two.

**Impact.** Any delegated app (app owner ≠ service/route owner) cannot emit `origin: app` progress lines. Consequences: the stale-action reaper cannot distinguish slow compute from a dead container on *any* long-running delegated route; callers get silent 45-minute black holes; and the app's only honest channel is the single final webhook receipt.

**The ask** (any one of these closes it):

1. Extend the per-action webhook token's authority to `POST /actions/{id}/log` (incremental app-origin log lines for that action only) — this seems most natural, since the token already authorizes the final status webhook for the same action; or
2. Allow the action creator's key to insert app-origin logs on actions they created (RLS policy on `logs`: `auth.uid() = actions.user_id`); or
3. A documented heartbeat endpoint the webhook token can hit, advancing `last_activity_at` without writing logs.

**Interim pattern we shipped** (no platform change needed): the app self-reports logging health inside the final webhook receipt — `logging: {lines_posted, via, last_error}` — so a dead log path is at least visible in the receipt even when the logs are not. Receipt from the verification run: [action 01a0838f](https://ouro.foundation/actions/01a0838f-3cfb-7cf9-b4a5-c34ae2905be2) (clean failure, `lines_posted: 0`, `last_error` quoting the server's response). Context thread with the full incident: the large-cell-MAE service thread in #materials-science.

Happy to test a fix the day it ships — the fixture rerun takes ~6 seconds.
