con un clic
db-policy-apply
Install KISS or granular RLS policies on a table
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Menú
Install KISS or granular RLS policies on a table
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Basado en la clasificación ocupacional SOC
Execute story development with selectable automation modes to accommodate different developer preferences, skill levels, and story complexity.
Set up a Kord-aligned documentation baseline (no legacy frameworks)
Create Next Story Task methodology and workflow
Validate Next Story Task methodology and workflow
advanced-elicitation methodology and workflow
No checklists needed - this task facilitates brainstorming sessions, validation is through user interaction methodology and workflow
| name | db-policy-apply |
| description | Install KISS or granular RLS policies on a table |
| agent | architect |
| subtask | false |
Install KISS or granular RLS policies on a table
Parameter:* mode (optional, default: interactive)
Purpose:* Definitive pass/fail criteria for task completion
Checklist:*
acceptance-criteria:
- [ ] Data persisted correctly; constraints respected; no orphaned data
type: acceptance-criterion
blocker: true
validation: |
Assert data persisted correctly; constraints respected; no orphaned data
error_message: "Acceptance criterion not met: Data persisted correctly; constraints respected; no orphaned data"
Strategy:* abort
Common Errors:*
Error:* Connection Failed
Error:* Query Syntax Error
Error:* Transaction Rollback
Token Savings: 89% | Time Savings: ~85%*
# Use the rls-policy-installer script
./Squads/super-agentes/scripts/database-operations/rls-policy-installer.sh {table} {mode}
# Examples:
./Squads/super-agentes/scripts/database-operations/rls-policy-installer.sh minds kiss
./Squads/super-agentes/scripts/database-operations/rls-policy-installer.sh sources read-only
./Squads/super-agentes/scripts/database-operations/rls-policy-installer.sh fragments private
# Available modes: kiss, read-only, private, team, custom
# Benefits:
# - Standardized policy templates
# - Automatic testing after installation
# - Safety checks for existing policies
# - 89% token savings
OR continue with manual policy installation below:*
table (string): Table name to apply policy tomode (string): 'kiss' or 'granular' - policy typeCheck table exists and mode is valid:
echo "Validating inputs..."
# Check table exists
psql "$SUPABASE_DB_URL" -c \
"SELECT EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_schema = 'public' AND table_name = '{table}'
);" | grep -q t || {
echo "❌ Table '{table}' not found"
exit 1
}
# Check mode
if [[ "{mode}" != "kiss" && "{mode}" != "granular" ]]; then
echo "❌ Invalid mode: {mode}"
echo " Use 'kiss' or 'granular'"
exit 1
fi
echo "✓ Table exists: {table}"
echo "✓ Mode: {mode}"
Display current RLS status:
echo "Checking existing RLS policies..."
psql "$SUPABASE_DB_URL" << EOF
SELECT
schemaname,
tablename,
policyname,
permissive,
roles,
cmd,
qual,
with_check
FROM pg_policies
WHERE tablename = '{table}';
EOF
echo ""
echo "RLS enabled on {table}?"
psql "$SUPABASE_DB_URL" -c \
"SELECT relrowsecurity FROM pg_class WHERE relname = '{table}';" \
| grep -q t && echo "✓ Yes" || echo "⚠️ No (will be enabled)"
Present policy that will be applied based on mode:
If mode = 'kiss':*
Will apply KISS policy to {table}:
- Enable RLS
- Single policy: users can only access their own rows
- Uses: (select auth.uid()) = user_id [PERFORMANCE OPTIMIZED]
- Applies to: SELECT, INSERT, UPDATE, DELETE
⚠️ CRITICAL PERFORMANCE NOTE:
Wrapping auth.uid() in SELECT provides 99.99% performance improvement
by allowing PostgreSQL to cache the function result.
Continue? (yes/no)
If mode = 'granular':*
Will apply granular policies to {table}:
- Enable RLS
- Separate policies for each operation (SELECT, INSERT, UPDATE, DELETE)
- Fine-grained control
- Uses: auth.uid() = user_id
Continue? (yes/no)
Get confirmation before proceeding.
Based on mode, generate appropriate SQL:
KISS Mode:*
-- Enable RLS
ALTER TABLE {table} ENABLE ROW LEVEL SECURITY;
-- Drop existing policies (if any)
DROP POLICY IF EXISTS "{table}_policy" ON {table};
-- Create single KISS policy (PERFORMANCE OPTIMIZED)
CREATE POLICY "{table}_policy"
ON {table}
FOR ALL
TO authenticated
USING (
-- ✅ CRITICAL: Wrap auth.uid() in SELECT for 99.99% performance gain
-- This allows PostgreSQL to cache the function result per statement
(select auth.uid()) IS NOT NULL AND
(select auth.uid()) = user_id
)
WITH CHECK (
(select auth.uid()) IS NOT NULL AND
(select auth.uid()) = user_id
);
-- Add helpful comment
COMMENT ON POLICY "{table}_policy" ON {table} IS
'KISS policy: users can only access their own rows (performance optimized with cached auth.uid())';
Granular Mode (PERFORMANCE OPTIMIZED):*
-- Enable RLS
ALTER TABLE {table} ENABLE ROW LEVEL SECURITY;
-- Drop existing policies (if any)
DROP POLICY IF EXISTS "{table}_select" ON {table};
DROP POLICY IF EXISTS "{table}_insert" ON {table};
DROP POLICY IF EXISTS "{table}_update" ON {table};
DROP POLICY IF EXISTS "{table}_delete" ON {table};
-- SELECT: Users read own rows
-- ✅ Wrapping auth.uid() in SELECT provides 99.99% performance improvement
CREATE POLICY "{table}_select"
ON {table}
FOR SELECT
TO authenticated
USING (
(select auth.uid()) IS NOT NULL AND
(select auth.uid()) = user_id
);
-- INSERT: Users create own rows
CREATE POLICY "{table}_insert"
ON {table}
FOR INSERT
TO authenticated
WITH CHECK (
(select auth.uid()) IS NOT NULL AND
(select auth.uid()) = user_id
);
-- UPDATE: Users update own rows
CREATE POLICY "{table}_update"
ON {table}
FOR UPDATE
TO authenticated
USING (
(select auth.uid()) IS NOT NULL AND
(select auth.uid()) = user_id
)
WITH CHECK (
(select auth.uid()) IS NOT NULL AND
(select auth.uid()) = user_id
);
-- DELETE: Users delete own rows
CREATE POLICY "{table}_delete"
ON {table}
FOR DELETE
TO authenticated
USING (
(select auth.uid()) IS NOT NULL AND
(select auth.uid()) = user_id
);
-- Add helpful comments
COMMENT ON POLICY "{table}_select" ON {table} IS 'Users can read own rows (cached auth.uid())';
COMMENT ON POLICY "{table}_insert" ON {table} IS 'Users can insert own rows (cached auth.uid())';
COMMENT ON POLICY "{table}_update" ON {table} IS 'Users can update own rows (cached auth.uid())';
COMMENT ON POLICY "{table}_delete" ON {table} IS 'Users can delete own rows (cached auth.uid())';
Save policy SQL to migration file:
TS=$(date +%Y%m%d%H%M%S)
MIGRATION_FILE="supabase/migrations/${TS}_rls_${mode}__{table}.sql"
mkdir -p supabase/migrations
cat > "$MIGRATION_FILE" << 'EOF'
-- Migration: Apply {mode} RLS policy to {table}
-- Generated: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
-- Table: {table}
-- Mode: {mode}
BEGIN;
[... SQL from step 4 ...]
COMMIT;
EOF
echo "✓ Migration created: $MIGRATION_FILE"
Use existing db-apply-migration task:
echo "Applying migration..."
# Execute db-apply-migration task internally
# (This will create snapshots, apply, verify)
Verify policies work correctly:
echo "Testing RLS policies..."
# Test 1: Anonymous user should see nothing
psql "$SUPABASE_DB_URL" << EOF
SET ROLE anon;
SELECT COUNT(*) AS anon_count FROM {table};
RESET ROLE;
EOF
# Test 2: Authenticated user should see only their rows
# (Requires setting up test user - provide instructions)
echo ""
echo "✓ Policy tests complete"
echo " ⚠️ Manual testing recommended:"
echo " - Use impersonate to test as specific user"
echo " - Verify each operation (SELECT, INSERT, UPDATE, DELETE)"
Display summary:
✅ RLS POLICY APPLIED
Table: {table}
Mode: {mode}
Migration: supabase/migrations/{TS}_rls_{mode}__{table}.sql
Policies: [list created policies]
Next steps:
1. Test policies manually: impersonate {user_id}
2. Run RLS audit: rls-audit
3. Update documentation
4. Commit migration to git
KISS* (Keep It Simple, Stupid):
Granular*:
Public Read, Authenticated Write (Performance Optimized):*
-- SELECT: Public
CREATE POLICY "{table}_select" ON {table}
FOR SELECT TO public
USING (true);
-- INSERT/UPDATE/DELETE: Authenticated users only
CREATE POLICY "{table}_write" ON {table}
FOR ALL TO authenticated
USING (
(select auth.uid()) IS NOT NULL AND
(select auth.uid()) = user_id
)
WITH CHECK (
(select auth.uid()) IS NOT NULL AND
(select auth.uid()) = user_id
);
Tenant-Based (Performance Optimized):*
CREATE POLICY "{table}_tenant" ON {table}
FOR ALL TO authenticated
USING (
(select auth.uid()) IS NOT NULL AND
tenant_id IN (
SELECT tenant_id FROM user_tenants
WHERE user_id = (select auth.uid())
)
);
-- ❌ DANGEROUS - User can modify this data!
CREATE POLICY "bad_policy" ON {table}
USING (
(auth.jwt() -> 'user_metadata' ->> 'role') = 'admin'
);
Why dangerous:* raw_user_meta_data can be modified by the user through Supabase Auth client. An attacker can set { "role": "admin" } and bypass security!
Safe alternative:* Use raw_app_meta_data (server-only):
-- ✅ SAFE - Only server can modify app_metadata
CREATE POLICY "safe_policy" ON {table}
USING (
(auth.jwt() -> 'app_metadata' ->> 'role') = 'admin'
);
Always check if user is authenticated:
-- ❌ Missing NULL check
USING (auth.uid() = user_id) -- Fails silently for anon users
-- ✅ Explicit authentication check
USING (
(select auth.uid()) IS NOT NULL AND
(select auth.uid()) = user_id
)
Enable RLS policies in SQL Editor (dev only):
-- Temporarily disable RLS for debugging (DANGEROUS - dev only!)
ALTER TABLE {table} DISABLE ROW LEVEL SECURITY;
-- Re-enable when done
ALTER TABLE {table} ENABLE ROW LEVEL SECURITY;
Table must have:
user_id UUID column (for user-based policies)tenant_id column (for tenant-based policies)CREATE INDEX idx_{table}_user_id ON {table}(user_id);If policy application fails:
rollback