RSL need Updating - 9 months of jury rigging

Here is one thread related to this unending problem that other platforms don’t have (According to Claude). I realize my app is pushing the limits but maybe a better long term fix is warranted? At this stage there is no way my App would be SCORM or HIPPA compliant. Please consider. I have so many “workarounds” now that I fear the worst if I decide to scale with Base44 rather than to rebuild on a more robust stack.

The setup

Building a public-facing booking flow on Base44. Anonymous booking visito...

The setup Building a public-facing booking flow on Base44. Anonymous booking visitors can't read Brand records via asServiceRole due to RLS. To bridge the gap, I built a new entity PublicBrandSnapshot (read: open, write: super_admin only). A function updatePublicBrandSnapshot rebuilds the cache for a given brand_id. A function backfillPublicBrandSnapshots iterates all brands and invokes the per-brand updater for each.

App: Telos (ID: 68acb57f0254697dfe7fbbfe) App Link: https://app.base44.com/apps/68acb57f0254697dfe7fbbfe/editor/preview

May 2, 4:24 PM

Thanks for the detailed setup description, Ed! I can see the pattern you're using here — a public snapshot entity as a workaround for the

asServiceRole

RLS issue. Looks like a solid architectural approach given the bug you're working around.

I notice you have an open ticket (#69ee860a50bd3c668b174ce8) that's already been escalated regarding the

asServiceRole

RLS bypass issue. It sounds like this ticket is related to the same underlying problem.

It seems like your message got cut off — "More to come" suggests you were about to share more details. What did you want to add or ask about? For example:

  • Are you hitting an issue with the

    updatePublicBrandSnapshot

    or

    backfillPublicBrandSnapshots

    functions?

  • Is there a specific error you're seeing?

  • Do you need help with the logic or structure of the snapshot pattern?

Go ahead and share the rest — happy to help!

May 2, 4:24 PM

The problem When backfill calls asServiceRole.functions.invoke('updatePublicBrandSnapshot', { brand_id }), the inner function's auth.me() returns a synthetic service user (service+...@no-reply.base44.com, role=admin, no workspace_id). Inside the function, base44.asServiceRole.entities.Brand.get(brand_id) returns 404 "not found" for every brand — even brands that demonstrably exist and are owned by the calling admin.

May 2, 4:24 PM

This is really helpful detail, Ed. What you're describing —

asServiceRole

returning 404s on entity reads inside a function invoked by another function via

asServiceRole

— sounds like it could be related to the same bug you already have escalated in ticket #69ee860a50bd3c668b174ce8.

The key observation here is the chained service role context: when

backfillPublicBrandSnapshots

calls

functions.invoke('updatePublicBrandSnapshot', { brand_id })

via

asServiceRole

, the inner function receives the synthetic service user context — but it seems that context isn't carrying the right app/workspace scope needed to resolve Brand records.

A few things worth noting:

  1. The 404 vs. permission error distinction — if it were a pure RLS block you'd typically expect a 403 or empty result, not a 404. A 404 suggests the entity lookup itself is failing, possibly because the service role context in the inner function is scoped to the wrong app or is missing workspace context entirely.

  2. The synthetic service user having no

    workspace_id

    is a red flag — Brand records may be workspace-scoped, and without that context the lookup could be resolving against the wrong namespace.

  3. Workaround to test: Instead of chaining

    asServiceRole.functions.invoke

    , try having

    backfillPublicBrandSnapshots

    do the Brand reads and snapshot writes directly within itself, without delegating to the inner function. This would tell you whether the issue is specifically with the chained invocation context vs. something else.

Given this is a deeper platform-level issue with chained

asServiceRole

function calls, I'd strongly recommend adding this specific detail — the chained invocation pattern and the 404 behavior — as a follow-up in your existing escalated ticket (#69ee860a50bd3c668b174ce8) so the team investigating has the full picture.

Would you like help structuring that update, or is there more you wanted to share first?

May 2, 4:24 PM

let me post all the statements before you respond. Thanks

May 2, 4:24 PM

Got it, go ahead!

May 2, 4:24 PM

What we ruled out The brand records definitely exist — Brand.list() returns them with valid IDs, names, workspace_ids, and owner_user_ids. The IDs from list are then passed directly to get and fail. Audit confirmed all required fields are populated. Auth gate passes. Workspace filtering works correctly. asServiceRole bypass on writes works (the snapshot entity itself has been tested separately).

May 2, 4:24 PM

Understood, standing by for the rest!

May 2, 4:25 PM

The specific contradiction Brand.list() and Brand.get(brand_id) should be consistent for the same auth context. They aren't. With the same exact ID, list returns the record but get returns 404. This happens both with base44.entities.Brand.get(...) and base44.asServiceRole.entities.Brand.get(...). Identical behavior across both paths suggests something deeper than RLS.

May 2, 4:25 PM

Got it, keep going!

May 2, 4:25 PM

The question for Support Why does asServiceRole.entities.Brand.get(brand_id) return 404 inside a function that was invoked via asServiceRole.functions.invoke(), when the brand_id was obtained from Brand.list() in the calling function moments earlier? Is service-to-service function invocation supposed to preserve a usable auth context for entity reads? Is there a recommended pattern for one function to call another function with admin-level entity-read privileges?

May 2, 4:25 PM

Thanks for laying that out so clearly, Ed. I have the full picture now.

The contradiction you've identified is precise and well-documented:

Brand.list()

and

Brand.get(brand_id)

returning inconsistent results for the same auth context, with the same ID, is a platform-level bug — not something you can architect around at the app level.

To directly answer your questions based on what's documented:

  • Is service-to-service invocation supposed to preserve a usable auth context? The SDK docs indicate

    createClientFromRequest

    in a backend function extracts auth from injected headers, and

    asServiceRole

    should provide admin-level access. There's no documented caveat about chained invocations degrading that context — so what you're experiencing appears to be an undocumented failure mode.

  • Is there a recommended pattern for one function calling another with admin-level reads? There isn't a documented pattern specifically for this scenario, which itself is worth flagging.

Given that this is clearly related to your already-escalated ticket (#69ee860a50bd3c668b174ce8), I'd recommend adding this as a follow-up there with exactly what you've written here. The team investigating that bug needs to know:

  1. list()vs

    get()

    inconsistency with identical IDs and auth context

  2. The chained

    asServiceRole.functions.invoke()

    pattern as the trigger

  3. That both

    entities.Brand.get()

    and

    asServiceRole.entities.Brand.get()

    fail identically — ruling out RLS as the cause

Your documentation here is excellent and gives the engineering team exactly what they need to reproduce and diagnose this. Shall I escalate this ticket and link it to the existing one?

May 2, 4:25 PM

Identifying details App ID: 68acb57f0254697dfe7fbbfe. My user: edferrigan@gmail.com (super_admin). Functions involved: backfillPublicBrandSnapshots, updatePublicBrandSnapshot. A specific failing brand_id you can investigate: 69a5ff8c42fa09d7c5b2b7e6 (EFC, in my workspace 6903ce3d04c105a16123f6ec, owned by my user 68acb57f0254697dfe7fbbff). Diagnostic logs in the function dashboard tagged [updatePublicBrandSnapshot] DIAG.

May 2, 4:25 PM

Thanks Ed — that's everything the team needs to investigate. Let me escalate this now with all the details you've provided.

May 2, 4:25 PM

Following up on ticket #69ee860a50bd3c668b174ce8 with new evidence. Implemented the bot's recommended workaround: inlined the snapshot-build logic directly inside backfillPublicBrandSnapshots so it no longer chains asServiceRole.functions.invoke to a second function. Brand reads under admin user context now work — all 11 brands in my workspace were read successfully.

May 2, 4:37 PM

But the writes failed. asServiceRole.entities.PublicBrandSnapshot.create() returns "Permission denied for create operation on PublicBrandSnapshot entity" for every brand. The entity's create RLS is restricted to super_admin only. My user (edferrigan@gmail.com) IS super_admin. asServiceRole should bypass RLS entirely.

May 2, 4:37 PM

This appears to be the same chained-asServiceRole pattern surfacing on the write path now instead of the read path. Reads worked when un-chained; writes failed even un-chained. Suggests asServiceRole is unreliable in this app for both reads AND writes, regardless of chaining.

May 2, 4:37 PM

App ID: 68acb57f0254697dfe7fbbfe. Function: backfillPublicBrandSnapshots. Entity: PublicBrandSnapshot. I'm pausing booking-flow work until I hear from a human handler. Happy to provide additional logs or test cases. Thanks.

May 2, 4:38 PM

Stephen

Support Team

Hi Ed,

This is a platform architecture constraint rather than a bug. When a backend function calls

asServiceRole.functions.invoke('updatePublicBrandSnapshot', ...)

, the inner function receives a bare HTTP request with no auth headers forwarded.

createClientFromRequest(req)

inside the inner function has no app context to work from, so all entity operations fail, including

asServiceRole

ones. That's why

Brand.get()

returns 404 for IDs that

Brand.list()

returned moments earlier — the outer and inner functions are running with completely different auth contexts.

The fix is to stop treating

updatePublicBrandSnapshot

as something you call from another function. Move the Brand read and snapshot upsert logic directly into

backfillPublicBrandSnapshots

as an inline loop.

updatePublicBrandSnapshot

can stay as a standalone HTTP-callable function for single-brand cache refreshes triggered from the frontend or a scheduled automation, but it should not be invoked from another backend function.

Here's the prompt to restructure the backfill:

In

backfillPublicBrandSnapshots

, replace the loop that calls

asServiceRole.functions.invoke('updatePublicBrandSnapshot', { brand_id })

with inline logic. For each brand returned by

Brand.list()

, directly read the brand record and upsert the corresponding

PublicBrandSnapshot

entry, all within this function using

asServiceRole

. Remove the

invoke

call entirely. Do not modify

updatePublicBrandSnapshot

itself.

Once restructured, the backfill will run entirely within a single auth context and the 404 issue will be gone.

Let us know how it goes.

Best regards, Stephen | Base44 Technical Support

May 2, 8:48 PM

Thanks Stephen. Quick clarification: we already restructured backfill to inline the logic per the bot's earlier guidance, before your reply. After that change, all 11 brand reads succeeded under regular admin auth — the 404s are gone. But all 11 PublicBrandSnapshot writes still failed with "Permission denied for create operation" via asServiceRole.entities.PublicBrandSnapshot.create(). The entity's create RLS is super_admin only, and my user is super_admin. Is the write-side failure a sep

May 3, 11:23 AM

Is the write-side failure a separate issue, or does your recommendation also resolve it once a single auth context is established?

May 3, 11:24 AM

Terryon

Support Team

Hi Ed,

It’s good to hear that the reads are functioning correctly after inlining the logic.

Regarding the write failure, I want to clarify how

asServiceRole

is designed to function. It is supposed to bypass RLS (Row Level Security) entirely, regardless of the specific rules in place (such as

super_admin

restrictions). If

asServiceRole.entities.PublicBrandSnapshot.create()

is returning a "Permission denied" error, it suggests a platform-level issue where the bypass isn't being applied correctly to writes in your application.

This appears to be a separate anomaly from the chained invocation problem we discussed. To help us narrow down the cause, could you provide the following details:

  1. Exact RLS Configuration: Can you confirm the specific

    create

    rule for

    PublicBrandSnapshot

    ? Is it currently set to

    user_condition: { role: 'super_admin' }

    or a different parameter?

  2. Frontend Test: If you call

    entities.PublicBrandSnapshot.create()

    directly from the frontend (without using

    asServiceRole

    ) while logged in as your user, does it work? This will help us determine if the failure is exclusive to the

    asServiceRole

    context within a function.

  3. Diagnostic Logs: Does the function dashboard show any additional metadata or error details beyond the standard "Permission denied" message?

Since this is the second unexpected behavior involving

asServiceRole

in this app, getting these details will be essential for our investigation.

Best regards,

Terry | Base44 Technical Support

May 3, 12:49 PM

MESSAGE 1 of 5:

Hi Terry — Ed Ferrigan, picking back up on this thread. Sorry for the delay. I have answers to all three of your questions, including a focused diagnostic test that isolates the failure mode. Sending across multiple messages so each fits.

May 5, 5:40 PM

MESSAGE 2 of 5:

Q1: Exact RLS create rule for PublicBrandSnapshot:

"create": { "user_condition": { "role": "super_admin" } }

Update/delete are identical. Read is open ({}).

Calling user (edferrigan@gmail.com) is super_admin. So per the rule, this user should be able to create.

May 5, 5:40 PM

MESSAGE 3 of 5:

Q2: Frontend test — user-scoped (NOT asServiceRole):

Called base44.entities.PublicBrandSnapshot.create({brand_id:"TEST_..."}) while authenticated as super_admin.

Result: SUCCESS. Record created with id 69fa7f3216f3799192b87ee2, created_by_id matched my user. Record deleted afterwards.

So the RLS rule itself works correctly through the user-scoped path.

May 5, 5:40 PM

MESSAGE 4 of 5:

Q3: asServiceRole test with full error capture:

Built diagnostic function (diagPublicBrandSnapshotWrite) that calls base44.asServiceRole.entities.PublicBrandSnapshot.create() with identical payload, same authenticated session.

Result: 403. Full error: { message: "Permission denied for create operation on PublicBrandSnapshot entity", name: "Base44Error", status: 403 }

No code, no responseData, no cause. Function logs show ZERO [diagPBS] console.log lines fired — only Deno star

May 5, 5:41 PM

Deno startup logs. The 403 appears to be thrown by the RLS layer before the function body executes.


MESSAGE 5 of 5:

Summary of the contradiction:

  • Same user (super_admin)

  • Same RLS rule

  • User-scoped: works

  • asServiceRole: 403 Permission denied

asServiceRole is not satisfying user_condition: super_admin on this entity — it appears to be evaluated as if no role is present.

Diag function diagPublicBrandSnapshotWrite is left deployed if you want to run more tests. Happy to add more ins

May 5, 5:42 PM

Yelyzaveta

Support Team

Hi there!

Thank you for your incredibly detailed breakdown. Your identification of the failure mode—specifically how the user-scoped path works while the asServiceRole path returns a 403 Forbidden—was instrumental in pinpointing the root cause.

I sincerely apologize for any confusion caused by our earlier responses. After a thorough review of the platform architecture, we’ve determined this is not a platform bug but relates to how the asServiceRole context is evaluated against RLS rules.

The core issue concerns how asServiceRole interacts with Role-Based Access Control (RBAC) within your RLS setup.

The Conflict: Your RLS rule for the PublicBrandSnapshot entity explicitly requires a super_admin role:

{"user_condition": {"role": "super_admin"}} 

The asServiceRole Context: When you use asServiceRole inside a backend function, the platform creates a synthetic service user. While this user has elevated privileges, it does not automatically inherit the super_admin role of the original user who triggered the function.

The Requirement: For asServiceRole to bypass or satisfy RLS rules that check for specific roles, the entity’s RLS must include permissions for the admin role.

Because your rule strictly requires super_admin, the service role—which identifies as an admin level service user—is being blocked by the RLS layer before the function logic even executes.

The Solution: To enable your backfillPublicBrandSnapshots function to write to the PublicBrandSnapshot entity using asServiceRole, you should update the RLS rule to include the admin role:

{"user_condition": {"role": "admin"}} 

Since super_admin encompasses admin permissions, your manual frontend tests will remain successful, and the asServiceRole context will now have the necessary authorization to perform create and update operations.

Given that you’ve already inline the logic to avoid chained invocation issues, updating this RLS rule should be the final step to enable your booking flow.

We appreciate your patience and the professional approach you took in documenting these findings. Please let us know if you encounter any further issues once the RLS is updated. We're here to help!

Best regards,

Liz | Base44 Support Team

May 6, 1:06 AM

1 of 7 Q1: Stephen's earlier reply (5/3) said chained asServiceRole.functions.invoke calls forward no auth headers, so the inner function has no auth context at all. Your reply says the synthetic user IS authenticated, just at admin level. Are these both correct (different mechanisms for chained vs direct asServiceRole), or is one of them outdated?

May 6, 9:10 AM

Q2: Does the synthetic admin user created by asServiceRole have a workspace_id? Our canonical RLS shape pairs admin role with a workspace_id match (admin AND data.workspace_id == {{user.data.workspace_id}}). If the synthetic user has no workspace_id, that clause still blocks the write. Should we use unscoped admin, or is there a synthetic workspace_id we can match?

May 6, 9:11 AM

3 of 6 Q3: If we add an unscoped admin clause to satisfy asServiceRole, that clause also lets any real admin user from any workspace create records on this entity. For PublicBrandSnapshot the risk is low (data is public by design and our function scope-filters before writing). But is that the intended pattern, or is there a way to authorize asServiceRole specifically without weakening the rule for real users?

May 6, 9:11 AM

4 of 6: Q4: Is this an entity-by-entity fix, or platform-wide? We have ~17 admin save sites that call updatePublicBrandSnapshot from admin browser context (real super_admin user, not asServiceRole). Should those continue working under the current rule, and we only need to add the admin clause for the asServiceRole-internal write paths?

May 6, 9:12 AM

5 of 6: Q5: Are there other entities in our app where asServiceRole writes will silently fail under super_admin-only RLS? If so, we'd want to audit them deliberately rather than discover the failures one at a time. Any guidance on identifying the affected entities would save us troubleshooting cycles.

May 6, 9:12 AM

Once I understand the answers, I'll update PublicBrandSnapshot's RLS with the right shape and verify the backfill works. Thanks for the patience on the back-and-forth — getting this right at the architectural level matters more than getting it right just for one entity. Thank you so much for your support on this!

May 6, 9:13 AM

Fode

Support Team

Hi Ed,

Thank you for the structured questions, I have direct answers to all five.

Q1: Stephen's reply vs. Liz's reply — which is correct?

Both are correct, but they describe two separate failure modes you hit sequentially.

Stephen's explanation applies to the chained invocation phase. When

backfillPublicBrandSnapshots

called

asServiceRole.functions.invoke('updatePublicBrandSnapshot', ...)

, the inner function received a raw HTTP request with no forwarded auth headers.

createClientFromRequest(req)

inside that function had no app or workspace context, so all entity operations failed — including

asServiceRole

ones. That's the source of the original 404s on Brand reads.

Liz's explanation applies after you inlined the logic. With a single function and a direct

asServiceRole

context, auth is intact — but

asServiceRole

creates a synthetic service user whose role evaluates as

admin

, not

super_admin

. Your RLS rule

{"user_condition": {"role": "super_admin"}}

does not match that synthetic user, so writes return 403. Reads worked because your Brand read RLS doesn't enforce a role check that blocks

admin

. These are genuinely separate mechanisms and you've now hit both.

Q2: Does the synthetic admin user have a

workspace_id

?

No. The synthetic user carries no

workspace_id

. This means a compound rule like:

{"$and": [{"user_condition": {"role": "admin"}}, {"data.workspace_id": "{{user.data.workspace_id}}"}]} 

...will always block the service user, because

{{user.data.workspace_id}}

resolves to null for the synthetic user. For any entity where

asServiceRole

needs to write, you need an unscoped

admin

branch (role check only, no workspace match) as a separate

$or

condition — not wrapped in an AND.

Q3: Is there a way to authorize

asServiceRole

specifically without weakening security for real users?

The platform does not provide a dedicated

asServiceRole

identity token you can target in an RLS condition. The synthetic user presents as

role: admin

, which is indistinguishable from a real admin user in RLS evaluation.

The safest pattern is an

$or

structure:

{"$or": [{"user_condition": {"role": "super_admin"}}, {"user_condition": {"role": "admin"}}]} 

For

PublicBrandSnapshot

this is fine — data is public by design and your function scope-filters before writing. For other entities where cross-workspace admin writes would be dangerous, the recommended workaround is to not rely on

asServiceRole

writes directly — instead, validate the scope in your function code and only write records that belong to the calling user's workspace before invoking the entity operation. This keeps the dangerous writes off the

asServiceRole

path entirely.

Q4: Entity-by-entity or platform-wide? Will your 17 admin save sites still work?

Entity-by-entity. Your 17 admin save sites call from the browser as a real authenticated

super_admin

user — they don't go through

asServiceRole

and will continue to satisfy the existing

super_admin

rule unchanged. Adding the

$or

with an

admin

branch does not affect those browser-origin calls at all.

Q5: Which other entities will silently fail under

asServiceRole

writes?

There's no automated audit tool for this. We manually reviewed your entity schemas and identified the affected set, entities where at least one write operation has a

super_admin

-only rule with no

admin

branch:

  • PublicBrandSnapshot— create, update, delete

  • AppConfig— create, update, delete

  • DebugLog— delete

  • Order— delete

  • Challenge— update, delete

  • Experiment— update, delete

  • SavedSection— update, delete

Cross-reference these against your backend functions: any function calling

asServiceRole.entities.<EntityName>.create/update/delete()

on an entity in this list needs the

$or

fix. Entities where RLS is open or uses

created_by

/

workspace_id

field comparisons rather than a

super_admin

role check are not affected.

The fix for

PublicBrandSnapshot

is:

{ "create": {"$or": [{"user_condition": {"role": "super_admin"}}, {"user_condition": {"role": "admin"}}]}, "read": {}, "update": {"$or": [{"user_condition": {"role": "super_admin"}}, {"user_condition": {"role": "admin"}}]}, "delete": {"$or": [{"user_condition": {"role": "super_admin"}}, {"user_condition": {"role": "admin"}}]} } 

Once that's deployed, the backfill should run cleanly end-to-end. Let us know how it goes.

Best regards, Fofo | Base44 Support

May 6, 12:07 PM

ran trace: What's confirmed: asServiceRole cannot read Brand or Contact in production. ContactActivity, User, and ServiceType work because their RLS happens to have unconditional admin/super_admin branches. Brand and Contact have the scoped-admin branch (which fails for the synthetic user).

May 7, 1:56 PM

The mitigating reality: The dev needs to confirm this, but in your app, who has role: admin versus role: user versus role: super_admin? If the only people with role: admin are platform operators (you in your super_admin capacity, plus maybe one or two trusted accounts), then in practice this branch only gets exercised by asServiceRole and there's no real exposure. But if role: admin is given to coaches by default, then this branch lets every coach read every other coach's brands.

May 7, 1:56 PM

That would be a serious privacy breach.

May 7, 1:57 PM

That was the AI. The answer is the entire app relies on other Coaches using it with users under them. SuperAdmin (me) and Admins (Coaches) and Users (Under Admins) same workspace. AI Suggested path: Path 1: Add an unscoped admin read branch to Brand and Contact RLS. The new read rule would look like: json"read": { "$or": [ { "user_condition": { "role": "super_admin" } }, { "user_condition": { "role": "admin" } }, { "$and": [ { "<OWNERSHIP>": "{{user.id}}" },

May 7, 2:01 PM

{ "data.workspace_id": "{{user.data.workspace_id}}" } ] } ] } What this means in practice:

The asServiceRole synthetic admin can now read brands and contacts (good — fixes the booking flow). A real human user with role: admin could ALSO read all brands and contacts across all workspaces (bad — this is a real privacy regression).

May 7, 2:02 PM

I gave Claude your entire thread. Not happy with this solution. Suggestions? Did it miss something?

May 7, 2:03 PM

Each coach and each users needs their own private entries across the board.

May 7, 2:05 PM

Surely there is a strategy for this. Can't image other apps not needing this?

May 7, 2:17 PM

Attached file

Please read the attached PDF and offer any feedback before I make the decision to move ALL my code over to a new platform if this cannot be repaired. It has stopped me so many times I can't count. Exhausting to spend my time this way. I saw an interview with your founder and this seems like the very thing he wants to fix now and not wait.

May 7, 2:24 PM

example what I keep running up against: "We're in a familiar pattern. The Brand read bug doesn't just break Calendar sync. It also breaks the writer that's supposed to populate the snapshot that's supposed to work around the Brand read bug. If the writer also can't read Brand, we have a chicken-and-egg situation that may need a different approach entirely (passing brand data into the writer instead of the writer fetching it)."

May 7, 4:23 PM

Stephen

Support Team

Hi Ed,

Good news:

backfillPublicBrandSnapshots

is already structured correctly. The inline approach you applied uses the caller's own session for reads and only routes the write through

asServiceRole

, which is exactly right.

The remaining issue is in

updatePublicBrandSnapshot

. Even though it verifies the caller is an admin before doing anything, it then switches to

base44.asServiceRole.entities.Brand.get(brand_id)

for the source read. That synthetic service user can't pass Brand's RLS, which is why it returns 404. The fix is straightforward: since you already have a verified admin caller, use their session for the Brand read.

In

updatePublicBrandSnapshot

, change the

Brand.get()

call from

base44.asServiceRole.entities.Brand.get(brand_id)

to

base44.entities.Brand.get(brand_id)

. Do the same for the subsequent

ServiceType

,

AppointmentSettings

,

WorkingHours

, and

BrandColor

reads in that function. Only keep

asServiceRole

on the

PublicBrandSnapshot

read and write calls at the end. The auth gate at the top of the function already confirms the caller is admin or super_admin, so using their session for source reads is safe and will satisfy Brand's RLS.

One separate thing worth noting: the

workspace_id

scope in Brand's RLS rule uses

{{user.workspace_id}}

, but for custom User fields the correct syntax is

{{user.data.workspace_id}}

. The scoped-admin branch is likely silently failing for all real admin users. Your current setup works because

BrandManager

filters by

owner_user_id

at the app level rather than relying on RLS for scoping, but the RLS rule itself is not doing what it looks like it should. Worth fixing to avoid surprises down the road.

Let us know how it goes.

Best regards, Stephen | Base44 Technical Support

May 7, 5:19 PM

Please inform me if there is any chance there is anything going on behind the scenes to fix RSL or make it more robust with suggestions in the document I added to my last post, or to continue patching for users? Thanks, and I do appreciate your help but Im exhausted running into this same issue and Im convinced other tech stacks don't have this issue.

May 7, 5:48 PM

Suzanne

Support Team

Hi Ed,

Thank you for everything you have shared throughout this thread — your documentation has been exceptional and the PDF you attached has been noted and passed along.

I want to answer your question directly and honestly.

Regarding whether there is work happening behind the scenes to make asServiceRole more robust and to address the RLS limitations you have been hitting — I genuinely do not have visibility into the product roadmap from the support side, and I would not want to make promises I cannot stand behind. What I can tell you is that the pattern you have documented — asServiceRole not carrying workspace context, the synthetic admin user being indistinguishable from real admin users in RLS evaluation, the chicken-and-egg read/write dependency — is a well-articulated architectural limitation and your ticket represents some of the best evidence we have seen for why it needs to be addressed.

The most direct way to make sure this reaches the product team with the weight it deserves is to post it at (feedback.base44.com)[feedback.base44.com] with the specifics you have outlined. The product team actively review that board and use it to prioritise what gets built.

On the immediate technical path forward — Stephen's latest suggestion to use the caller's session for Brand reads rather than asServiceRole is the correct fix for updatePublicBrandSnapshot and should unblock your backfill without the privacy risk of widening the RLS rule. The {{user.data.workspace_id}} correction he flagged is also worth applying.

Your frustration is completely understood and your feedback matters.

Kind regards, Suzanne | Base44

2:21 AM

Hi Stephen — your fix worked for the source reads. Brand, ServiceType, AppointmentSettings, WorkingHours, BrandColor all return data now and the snapshot payload builds correctly. Thank you.

The function then failed at the final step: PublicBrandSnapshot.create returned 403 "Permission denied" because asServiceRole is synthetic admin, not super_admin. So I tried applying your same pattern to the snapshot write.

10:04 AM

Specifically: I changed the PublicBrandSnapshot existing-record lookup AND the create/update at the end from base44.asServiceRole.entities.PublicBrandSnapshot.* to base44.entities.PublicBrandSnapshot.*. The auth gate already verifies super_admin, so I expected the caller's session to satisfy the create RLS.

After that change, the function fails MUCH earlier — auth.me() itself returns 403.

10:04 AM

The 403 response body is a Cloudflare "Just a moment..." challenge page (HTML), not Base44's own auth response. Logs show:

[DIAG auth.me result: user_present=false auth_error=Request failed with status code 403] [Base44 SDK Error] 403: Request failed with status code 403 Error data: "<!DOCTYPE html>...<title>Just a moment...</title>..."

Reverting only the snapshot calls back to asServiceRole brings auth.me() back to working.

10:05 AM

Two questions:

  1. Why does switching the PublicBrandSnapshot calls to the caller's session break auth.me() upstream? auth.me() runs before any entity calls. Is there something about mixing asServiceRole and caller-session calls in the same function that disrupts SDK initialization?

  2. The Cloudflare challenge page in the response — that suggests an edge-layer intercept before Base44's auth runs. Expected?

10:06 AM

Bonus question: what's the recommended pattern for a function auth-gated to super_admin that needs to write to an entity whose create RLS only allows super_admin? Keep the write on asServiceRole and loosen the entity's RLS to allow admin? Or another approach?

Test details: brand_id 69a5ff8c42fa09d7c5b2b7e6 (EFC), caller edferrigan@gmail.com (super_admin), function updatePublicBrandSnapshot.

Thank you. — Ed

10:06 AM

Offline support, prioritized reply within 48h

Handled by a support specialist

Please authenticate to join the conversation.

Upvoters
Status

In Review

Board
💡

Feature Request

Date

About 14 hours ago

Author

Ed Ferrigan

Subscribe to post

Get notified by email when there are changes.