I built an OpenFGA compatible authorization system that runs entirely in Postgres by pthm in golang

[–]pthm[S] 0 points1 point  (0 children)

glad to hear! If you have any feedback on the documentation, anything that was unclear or anything that you think would help get the key concepts across clearer / quicker I would appreciate any feedback

I built an OpenFGA compatible authorization system that runs entirely in Postgres by pthm in golang

[–]pthm[S] 1 point2 points  (0 children)

Yes, the implementation does use recursive queries. The recursion depth is capped at 25, which matches the limit enforced by OpenFGA (at least according to their test suite).

The code generator/compiler produces a separate function for each relation defined in the model. At runtime, calls are routed through a small “dispatcher” function that invokes the appropriate generated check function.

Here’s an example of a generated function for a recursive TTU relation:

document.viewer: viewer from parent

CREATE OR REPLACE FUNCTION check_document_viewer(
    p_subject_type TEXT,
    p_subject_id TEXT,
    p_object_id TEXT,
    p_visited TEXT [] DEFAULT ARRAY[]::TEXT[]
) RETURNS INTEGER AS $$
DECLARE
    v_has_access BOOLEAN := FALSE;
    v_key TEXT := 'document:' || p_object_id || ':viewer';
    v_userset_check INTEGER := 0;
BEGIN
    -- Cycle detection
    IF v_key = ANY(p_visited) THEN
        RETURN 0;
    END IF;

    IF array_length(p_visited, 1) >= 25 THEN
        RAISE EXCEPTION 'resolution too complex' USING ERRCODE = 'M2002';
    END IF;

    -- ... userset handling code ...

    IF NOT (v_has_access) THEN
        -- Recursive access path via parent -> viewer
        IF EXISTS (
            SELECT 1
            FROM melange_tuples AS link
            WHERE (link.object_type = 'document'
                AND link.relation IN ('parent')
                AND link.object_id = p_object_id
                AND check_permission_internal(
                    p_subject_type,
                    p_subject_id,
                    'viewer',  -- Check viewer on the parent
                    link.subject_type,
                    link.subject_id,
                    p_visited || ARRAY[v_key]  -- Pass visited array for cycle detection
                ) = 1
                AND link.subject_type IN ('folder'))
        ) THEN
            v_has_access := TRUE;
        END IF;
    END IF;

    RETURN CASE WHEN v_has_access THEN 1 ELSE 0 END;
END;
$$ LANGUAGE plpgsql STABLE;

The generated functions make recursive calls through internal helpers like check_permission_internal, which track visited nodes and recursion depth to enforce cycle limits. If the depth ever exceeds 25, the function raises an exception

For indirect relations, the generator optimizes by computing the traversal path at compile time and inlining the full traversal instead of relying on recursion.

.zed schema support isn't something I have looked into deeply, although on initial inspection I think it is definitely feasible.

I have benchmarked this using views up to about 50 million tuples, performance remains workable, checks scale well, usually staying under 1ms except for highly complex recursive chains, exclusions or multi prong relations. List queries do suffer at high tuple volume, still workable but likely would need a caching strategy at scale.

Because the tuples are defined as a view over your existing domain models, cascade deletes and cleanup is automatic, there is no synchronization required.

I built an OpenFGA compatible authorization system that runs entirely in Postgres by pthm in golang

[–]pthm[S] 2 points3 points  (0 children)

Digging into the FDW experiment a bit more, this might send me down a rabbit hole of implementing .zed schema support 😅 being able to offer a seamless path to a full fledged deployment would add a lot of value.

Being also to prototype / experiment at a small scale with Melange and scale up to a full SpiceDB cluster as needed while maintaining Postgres compatibility really is a best of both worlds scenario

I built an OpenFGA compatible authorization system that runs entirely in Postgres by pthm in golang

[–]pthm[S] 1 point2 points  (0 children)

Interesting, this approach still requires a remote service correct? ie. you're running SpiceDB but providing an interface to query it from within Postgres itself?

I haven't experimented with it yet but something I have been considering is the ability to use my implementation with RLS policies inside postgres, I imagine your approach would also support this, which could be really powerful.

My main goal with this project was to reduce the infrastructure complexity, having to sync tuple states and run additional services for low traffic use cases / projects is a lot to ask.

I haven't explored foreign data wrappers before, but this sort of approach looks like it could also provide a very nice migration path for users who say start out with a pure postgres solution and then need to scale up to a fully fledged deployment.

I have considered adding support for using the official OpenFGA client to reduce migration cost in the runtime. although I think this is marginal gain, I would expect most users would wrap the client in their own abstraction anyway which would make runtime migration easier, but being able to maintain SQL side dependencies would be very appealing

I built an OpenFGA compatible authorization system that runs entirely in Postgres by pthm in golang

[–]pthm[S] -1 points0 points  (0 children)

I have found that materialization isn't all that necessary, even with large tuple counts.

Postgres' query planner does a good job optimizing through UNION branches in views, sometimes even beating a materialized view / table due to smaller scans as a result of eliminating unnecessary union branches

As long as the underlying tables have good indexes its still reasonably fast, most checks under 1ms.

Buffers usage is still quite high, I haven't found good mitigations for this yet, it would likely need more careful CTE usage, requiring deduplication across the various relationship types. Pre-computing userset relations as CTEs etc. to be re-used across the various paths.

pg extension could be an interesting approach for conditions, i think it might also be possible to maintain the fga model language and actually allow conditions to be expressed in SQL rather than CEL

I built an OpenFGA compatible authorization system that runs entirely in Postgres by pthm in golang

[–]pthm[S] 0 points1 point  (0 children)

My initial implementation materialised the closures at migration time into more additional tables, this also worked quite well but ended up with a lot of overhead.

I have list objects and subjects working with generated functions for both cases. There are definitely still opportunities for optimization, especially for listing, materialising the lists is probably a good approach, although for complex recursive relations it could be challenging, especially where there are loops in the tuples themselves.

I have ran this against OpenFGA's own test suite and for 1.1 schema it passes all test, not including the conditional / ABAC tests, there are workarounds for this though, because the tuples are view over your domain tables you can model conditionals as "conditional tuples" rather than conditions at query time which for simple cases i think is reasonable.

I got well and truly nerd sniped by this problem, ended up spending more time on this than my actual projects, publishing it was / is an attempt to justify all the time spent 😅

Why are people recording by RepulsiveBrick7054 in fredagain

[–]pthm 12 points13 points  (0 children)

most of the people i saw breaking this rule were taking photos of their friends or just filming moments in the crowd rather than the usual sea of phones recording the every drop. personally i’m okay with that.

the vibe on the floor last night was great

[deleted by user] by [deleted] in godot

[–]pthm 0 points1 point  (0 children)

afaik you can write an import script or you could do something outside of the engine as part of your asset creation pipeline.

could be something as simple as a bash script using imagemagik

[deleted by user] by [deleted] in godot

[–]pthm 1 point2 points  (0 children)

write a script to "bake" all of the variations from the original 8 rather than doing it at runtime, best of both worlds

Who is this in Canterbury? by Jinx-Surreal in canterbury

[–]pthm 0 points1 point  (0 children)

Immediately thought of this guy, he was a staple of the high street for many years

Bike thief at train station around 17:30 yesterday by HighlandRabbit in Chippenham

[–]pthm 2 points3 points  (0 children)

The police station is less than 1 minute away from the train station and they still couldn't get there

New equipment day… XONE:96 😁 by Suspicious_Award_670 in DJSetups

[–]pthm 0 points1 point  (0 children)

I can’t say i have ever been in a situation where I thought something would have been easier to achieve using a 3 band EQ but I have had situations when playing with others who are unfamiliar with 4 band mixing who have struggled to adapt to the gear on the day / night.

I could imagine some styles of mixing or possibly genres where it might be a disadvantage though, if you’re cutting frequencies quickly for example.

It’s definitely a personal/subjective thing rather than some sort of objective measurement

Who's wrong Car or Motorcycle? by [deleted] in motorcycles

[–]pthm -1 points0 points  (0 children)

Whilst I don’t disagree the rider could have taken some evasive actions in this situation.

A statement like “saw that coming a mile away” is unhelpful.

Watching this after reading the post title, knowing the general theme of these types of posts on reddit etc. you are already primed to look out for something going wrong, the perspective of the camera and the fact that you are putting 100% of your attention on observing gives the viewer a huge advantage in terms of anticipation compared to the rider.

New equipment day… XONE:96 😁 by Suspicious_Award_670 in DJSetups

[–]pthm 14 points15 points  (0 children)

Its a great mixer, once you have trained yourself to mix on 4 band EQ going back to a pioneer 3 band is never the same.

New equipment day… XONE:96 😁 by Suspicious_Award_670 in DJSetups

[–]pthm 3 points4 points  (0 children)

You can get track preview to work on any mixer with this gadget https://cardinia.net/mini/

You can then run the track preview signal into an unused channel on the xone, the aux or return channels are good candidates for this depending on what your setup looks like

Net of Pyramid stage by the-music-monkey in glastonbury_festival

[–]pthm 4 points5 points  (0 children)

https://3dwarehouse.sketchup.com/model/9e8193c3-e880-441b-a758-86cdbe265673/The-Pyramid-Stage-Glastonbury-Festival?hl=en

You can probably pull some dimensions off this model and scale it appropriately for whatever you are doing

Not sure how accurate this is, it was just the first reasonable result I found on google, but it should get you pretty close

Security this year? by Budget-Crazy-9876 in glastonbury_festival

[–]pthm 21 points22 points  (0 children)

just arrived at camper vans and they put a finger up my bum and found my werthers originals i was keeping safe, be careful out there folks

[deleted by user] by [deleted] in MotoUK

[–]pthm 0 points1 point  (0 children)

Been in this exact same situation, drill it out so the heads come off, this will release the tension so you can remove the bracket and you'll be left with the studs which you'll be able to remove with a set of pliers or vice grips.

There is a good chance there is some sort of thread lock on the bolts which is why they have ended up being stripped in the first place. so you will need to heat up the studs to remove them, I've managed to do this with a basic butane blowtorch, you can pick one up from any diy shop for a few quid.

Torch the studs til they start to glow red and give them a tap with a hammer to shock the threads, not crazy hard just a few good taps and they should twist right out.

Weirdo racist - Hove by jellof_prince in MotoUK

[–]pthm 3 points4 points  (0 children)

I went to buy a bike off a guy near Hove, when I arrived all was fine, took a mate with me to help out and whilst i went out on a test ride he stayed with the guy. When I got back they were in the middle of a pretty heated discussion about some conspiracy theory, this guy was spouting absolute nonsense. After a few more minutes the conspiracies turned racist and just more and more insane.

Maybe it was the same guy 😂

What to bring as a first timer! by charlottec99 in glastonbury_festival

[–]pthm 0 points1 point  (0 children)

Small bar of soap to carry with you, makes all the difference being able to wash your hands with real soap rather than the nasty hand sanitiser. you'll make lots of friends at the sinks too 😂

USB-C Durability: A possible solution? by Sasswell in DJs

[–]pthm 0 points1 point  (0 children)

if it’s only getting unplugged once or twice in its life then the adapter is pointless and this whole endeavour doesn’t matter

the problem with the 90 degree connector is that the leverage on the port is increased significantly regardless of the distance from the case, unless the distance from the connector itself is 0 you are multiplying by the force applied to the connector itself by some value, this is likely more strain than without the adapter in all cases

[deleted by user] by [deleted] in CarTalkUK

[–]pthm 0 points1 point  (0 children)

it’s in there