# Bluefin is a capability system

**URL:** <https://discourse.haskell.org/t/bluefin-is-a-capability-system/14632>\
**Category:** Uncategorized\
**Created:** [September 1, 2026, 7:29am UTC](https://discourse.haskell.org/t/bluefin-is-a-capability-system/14632 "2026-09-01T07:29:01Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![tomjaguarpaw](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tomjaguarpaw/32/1230_2.png) [@tomjaguarpaw](https://discourse.haskell.org/u/tomjaguarpaw)\
**Post date:** [September 1, 2026, 7:29am UTC](https://discourse.haskell.org/t/bluefin-is-a-capability-system/14632/1 "2026-09-01T07:29:01Z")

</div>

I decided I am going to start describing Bluefin as a “capability system”. This article explains why:

- [Bluefin is a capability system](https://h2.jaguarpaw.co.uk/posts/bluefin-capability-system/)

---

<div class="post-metadata">

**Author:** ![bmobn00b](https://avatars.discourse-cdn.com/v4/letter/b/ecccb3/32.png) [@bmobn00b](https://discourse.haskell.org/u/bmobn00b)\
**Post date:** [September 1, 2026, 4:19pm UTC](https://discourse.haskell.org/t/bluefin-is-a-capability-system/14632/2 "2026-09-01T16:19:13Z")

</div>

[GitHub - tweag/capability: Extensional capabilities and deriving combinators · GitHub](https://github.com/tweag/capability) ur late to the party.

---

<div class="post-metadata">

**Author:** ![tomjaguarpaw](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tomjaguarpaw/32/1230_2.png) [@tomjaguarpaw](https://discourse.haskell.org/u/tomjaguarpaw)\
**Post date:** [September 1, 2026, 5:20pm UTC](https://discourse.haskell.org/t/bluefin-is-a-capability-system/14632/3 "2026-09-01T17:20:31Z")

</div>

The capability party is only just getting started!

---

<div class="post-metadata">

**Author:** ![mikeplus64](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/mikeplus64/32/3998_2.png) [@mikeplus64](https://discourse.haskell.org/u/mikeplus64)\
**Post date:** [September 2, 2026, 3:28am UTC](https://discourse.haskell.org/t/bluefin-is-a-capability-system/14632/4 "2026-09-02T03:28:12Z")

</div>

If I ever write an effect library it will be called `knobs`. [“handle” ~~\> knob]

---

<div class="post-metadata">

**Author:** ![wolverian](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/wolverian/32/1387_2.png) [@wolverian](https://discourse.haskell.org/u/wolverian)\
**Post date:** [September 2, 2026, 9:56am UTC](https://discourse.haskell.org/t/bluefin-is-a-capability-system/14632/5 "2026-09-02T09:56:34Z")

</div>

I like this change. It clarifies how to think about Bluefin to me. I’m not exactly sure why. Names can be powerful things.

---

<div class="post-metadata">

**Author:** ![tomjaguarpaw](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tomjaguarpaw/32/1230_2.png) [@tomjaguarpaw](https://discourse.haskell.org/u/tomjaguarpaw)\
**Post date:** [September 2, 2026, 11:24am UTC](https://discourse.haskell.org/t/bluefin-is-a-capability-system/14632/6 "2026-09-02T11:24:22Z")

</div>

Nice, thanks for the feedback and I’m glad you like the change. I agree that names can be powerful things. Like it or not, people often lean on them for intuition, sometimes unconsciously. Choosing good names can help a lot.

---

<div class="post-metadata">

**Author:** ![slow-dive](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/slow-dive/32/5585_2.png) [@slow-dive](https://discourse.haskell.org/u/slow-dive)\
**Post date:** [September 2, 2026, 11:40am UTC](https://discourse.haskell.org/t/bluefin-is-a-capability-system/14632/7 "2026-09-02T11:40:09Z")

</div>

I remember I was a bit confused by Tom’s “A History of Effect Systems” talk w/r/t `ReaderT IO`.

@tomjaguarpaw you mentioned for the `ReaderT` pattern that “everything is now in `IO`” / “no encapsulation”. But isn’t the ReaderT-IO-pattern used together with “capability classes” or “tagless final” or whatever people call it, so you only have the effects that are in your constraints?

---

<div class="post-metadata">

**Author:** ![tomjaguarpaw](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tomjaguarpaw/32/1230_2.png) [@tomjaguarpaw](https://discourse.haskell.org/u/tomjaguarpaw)\
**Post date:** [September 2, 2026, 12:26pm UTC](https://discourse.haskell.org/t/bluefin-is-a-capability-system/14632/8 "2026-09-02T12:26:04Z")

</div>

RIO suggests “`Has` classes”, which I guess is what you mean by “capability classes”:

```haskell
class HasConfig env where
  configL :: Lens' env Config -- more on this in a moment
myFunction :: HasConfig env => RIO env Foo

```

> **[rio](https://hackage.haskell.org/package/rio-0.1.25.0)**
>
> A standard library for Haskell

Here’s an example from the Stack source. You can use `withEnvConfig` to run a `RIO EnvConfig` blog inside a `RIO Config`, by providing some extra info that it needs, specifically the `NeedTargets` and the `BuildOptsCLI`.

> <https://github.com/commercialhaskell/stack/blob/master/src/Stack/Runners.hs#L108-L114>

But there’s nothing to prevent the `EnvConfig` from leaking:

```haskell
dodgy :: NeedTargets -> BuildOptsCLI -> RIO Config ()
dodgy nt opts = do
  envConfig <- withEnvConfig nt opts ask
  let _ = envConfig :: EnvConfig
  ...

```

Maybe not such a big deal when it’s a compiler configuration, but if it was a block that’s supposed to locally escalate privileges then that could be a big deal.

---

<div class="post-metadata">

**Author:** ![slow-dive](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/slow-dive/32/5585_2.png) [@slow-dive](https://discourse.haskell.org/u/slow-dive)\
**Post date:** [September 2, 2026, 1:48pm UTC](https://discourse.haskell.org/t/bluefin-is-a-capability-system/14632/9 "2026-09-02T13:48:45Z")

</div>

Thank you for the example, I see that this could happen. I haven’t used RIO specifically, what I was imagining was rather having functions like

`f :: (MonadRandom m) => m Int`

It doesn’t really matter if this would be implemented with a `ReaderT IO` stack in the end, I am still constrained to only use `MonadRandom` functions in `f` right? I thought this is what people did with the ReaderT pattern, maybe with domain-specific classes instead of `MonadState` / `MonadRandom` etc. (but I have no idea whether that is true 🙂 ).

---

<div class="post-metadata">

**Author:** ![tomjaguarpaw](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tomjaguarpaw/32/1230_2.png) [@tomjaguarpaw](https://discourse.haskell.org/u/tomjaguarpaw)\
**Post date:** [September 2, 2026, 2:10pm UTC](https://discourse.haskell.org/t/bluefin-is-a-capability-system/14632/10 "2026-09-02T14:10:58Z")

</div>

> [@slow-dive](#):
>
> It doesn’t really matter if this would be implemented with a `ReaderT IO` stack in the end, I am still constrained to only use `MonadRandom` functions in `f` right?

Yes, there’s no problem with what you are constrained to do in an effectful operation. The problem is whether effectful operations can leak out of the scope of the handler that introduces the effect. `withEnvConfig` is an example of a handler and my example above shows that the operations it introduces can leak out (by extracting an `EnvConfig`).

EDIT: I realised I’ve written an article on exactly this topic: [Bluefin prevents handles leaking](https://h2.jaguarpaw.co.uk/posts/bluefin-prevents-handles-leaking/)

---

<div class="post-metadata">

**Author:** ![kephas](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/kephas/32/3814_2.png) [@kephas](https://discourse.haskell.org/u/kephas)\
**Post date:** [September 2, 2026, 9:56pm UTC](https://discourse.haskell.org/t/bluefin-is-a-capability-system/14632/11 "2026-09-02T21:56:47Z")

</div>

Capabilities also combine designation and authority and ideally support attenuation. The Bluefin value for a state effect is a capability to one specific mutable state. Many other effects add some ambient authority.

If I have a KeyValueStore effect, it would be nice to turn it into a fine-grained capability that I can attenuate so that I can grant the authority to only read a specific key, or write a specific key, or create a new key.

---

<div class="post-metadata">

**Author:** ![wolverian](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/wolverian/32/1387_2.png) [@wolverian](https://discourse.haskell.org/u/wolverian)\
**Post date:** [September 3, 2026, 7:13am UTC](https://discourse.haskell.org/t/bluefin-is-a-capability-system/14632/12 "2026-09-03T07:13:03Z")

</div>

There is an [interesting comment](https://lobste.rs/c/1q81x6) on Lobste.rs, where I think the point is summarized in this sentence:

> The Pony StartProcessAuth is an authority not a capability, because it doesn’t designate the program to be started.

I didn’t know about this authority/capability terminology.

---

<div class="post-metadata">

**Author:** ![tomjaguarpaw](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tomjaguarpaw/32/1230_2.png) [@tomjaguarpaw](https://discourse.haskell.org/u/tomjaguarpaw)\
**Post date:** [September 3, 2026, 7:28am UTC](https://discourse.haskell.org/t/bluefin-is-a-capability-system/14632/13 "2026-09-03T07:28:02Z")

</div>

> [@wolverian](#):
>
> I didn’t know about this authority/capability terminology.

Nor me, thanks for sharing! I don’t see why that distinction is useful but perhaps someone will be able to explain it to me.

---

<div class="post-metadata">

**Author:** ![tomjaguarpaw](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tomjaguarpaw/32/1230_2.png) [@tomjaguarpaw](https://discourse.haskell.org/u/tomjaguarpaw)\
**Post date:** [September 3, 2026, 7:34am UTC](https://discourse.haskell.org/t/bluefin-is-a-capability-system/14632/14 "2026-09-03T07:34:49Z")

</div>

> [@kephas](#):
>
> If I have a KeyValueStore effect, it would be nice to turn it into a fine-grained capability that I can attenuate so that I can grant the authority to only read a specific key, or write a specific key, or create a new key.

Sure, Bluefin can do that:

```haskell
-- ghci> example
-- fromList [("good bye","C++"),("hello","world")]
example :: Map String String
example = runPureEff $ do
  let s =
        Map.fromList
          [ ("good bye", "C++"),
            ("hello", "kephas")
          ]
  Static.evalModify s $ \map -> do
    let _ = map :: Static.Modify (Map String String) _
    toDynamic map $ \dynamicMap -> do
      let _ = map :: Static.Modify (Map String String) _
      attenuateAtKey "hello" dynamicMap $ \helloValue -> do
        -- helloValue only allows us to read and write the value at
        -- key "hello"
        let _ = helloValue :: DynamicModify (Maybe String) _
        attenuateModifyToWrite helloValue $ \helloValueWrite -> do
          -- helloValueWrite only allows us to write the value at key
          -- "hello"
          let _ = helloValueWrite :: WriteOnly (Maybe String) _
          write helloValueWrite (Just "world")

    Static.get map

```

> **Full code**
>
> ```haskell
> {-# LANGUAGE GHC2021 #-}
> {-# LANGUAGE DerivingVia #-}
> {-# LANGUAGE PartialTypeSignatures #-}
> {-# OPTIONS_GHC -Wno-partial-type-signatures #-}
> 
> import Bluefin.Capability.Modify qualified as Static
> import Bluefin.Compound
> ( Handle,
> OneWayCoercible,
> OneWayCoercibleHandle (MkOneWayCoercibleHandle),
> makeOp,
> mapHandle,
> oneWayCoercibleImpl,
> oneWayCoercibleTrustMe,
> useImplIn,
> useImplUnder,
> )
> import Bluefin.Eff (Eff, runPureEff, (:&), (:>))
> import Data.Map.Strict (Map)
> import Data.Map.Strict qualified as Map
> 
> -- ghci> example
> -- fromList [("good bye","C++"),("hello","world")]
> example :: Map String String
> example = runPureEff $ do
> let s =
> Map.fromList
> [ ("hello", "kephas"),
> ("good bye", "C++")
> ]
> Static.evalModify s $ \map -> do
> let _ = map :: Static.Modify (Map String String) _
> toDynamic map $ \dynamicMap -> do
> let _ = map :: Static.Modify (Map String String) _
> attenuateAtKey "hello" dynamicMap $ \helloValue -> do
> -- helloValue only allows us to read and write the value at
> -- key "hello"
> let _ = helloValue :: DynamicModify (Maybe String) _
> attenuateModifyToWrite helloValue $ \helloValueWrite -> do
> -- helloValueWrite only allows us to write the value at key
> -- "hello"
> let _ = helloValueWrite :: WriteOnly (Maybe String) _
> write helloValueWrite (Just "world")
> 
> Static.get map
> 
> toDynamic ::
> (e1 :> es) =>
> Static.Modify s e1 ->
> (forall e. DynamicModify s e -> Eff (e :& es) r) ->
> Eff es r
> toDynamic m k =
> useImplIn k $
> DynamicModify
> { getImpl = Static.get m,
> putImpl = Static.put m
> }
> 
> get :: (e :> es) => DynamicModify s e -> Eff es s
> get h = makeOp (getImpl (mapHandle h))
> 
> put :: (e :> es) => DynamicModify s e -> s -> Eff es ()
> put h s = makeOp (putImpl (mapHandle h) s)
> 
> modify :: (e :> es) => DynamicModify s e -> (s -> s) -> Eff es ()
> modify h f = get h >>= put h . f
> 
> data DynamicModify s e = DynamicModify
> { getImpl :: forall e'. Eff (e' :& e) s,
> putImpl :: forall e'. s -> Eff (e' :& e) ()
> }
> deriving (Handle) via OneWayCoercibleHandle (DynamicModify s)
> 
> instance (e :> es) => OneWayCoercible (DynamicModify s e) (DynamicModify s es) where
> oneWayCoercibleImpl = oneWayCoercibleTrustMe $ \h ->
> DynamicModify
> { getImpl = useImplUnder (getImpl h),
> putImpl = useImplUnder . putImpl h
> }
> 
> data ReadOnly s e = ReadOnly
> { readImpl :: forall e'. Eff (e' :& e) s
> }
> deriving (Handle) via OneWayCoercibleHandle (ReadOnly s)
> 
> instance (e :> es) => OneWayCoercible (ReadOnly s e) (ReadOnly s es) where
> oneWayCoercibleImpl = oneWayCoercibleTrustMe $ \h ->
> ReadOnly {readImpl = useImplUnder (readImpl h)}
> 
> data WriteOnly s e = WriteOnly
> { writeImpl :: forall e'. s -> Eff (e' :& e) ()
> }
> deriving (Handle) via OneWayCoercibleHandle (WriteOnly s)
> 
> instance (e :> es) => OneWayCoercible (WriteOnly s e) (WriteOnly s es) where
> oneWayCoercibleImpl = oneWayCoercibleTrustMe $ \h ->
> WriteOnly {writeImpl = useImplUnder . writeImpl h}
> 
> attenuateModifyToRead ::
> (e1 :> es) =>
> DynamicModify s e1 ->
> (forall e. ReadOnly s e -> Eff (e :& es) r) ->
> Eff es r
> attenuateModifyToRead m k =
> useImplIn k $
> ReadOnly
> { readImpl = get m
> }
> 
> attenuateModifyToWrite ::
> (e1 :> es) =>
> DynamicModify s e1 ->
> (forall e. WriteOnly s e -> Eff (e :& es) r) ->
> Eff es r
> attenuateModifyToWrite m k =
> useImplIn k $
> WriteOnly
> { writeImpl = put m
> }
> 
> attenuateAtKey ::
> (Ord k, e1 :> es) =>
> k ->
> DynamicModify (Map k v) e1 ->
> (forall e. DynamicModify (Maybe v) e -> Eff (e :& es) r) ->
> Eff es r
> attenuateAtKey key m body =
> useImplIn body $
> DynamicModify
> { getImpl = Map.lookup key <$> get m,
> putImpl = \mv ->
> modify m (Map.alter (const mv) key)
> }
> 
> write :: (e :> es) => WriteOnly s e -> s -> Eff es ()
> write h s = makeOp (writeImpl (mapHandle h) s)
> 
> read :: (e :> es) => ReadOnly s e -> Eff es s
> read h = makeOp (readImpl (mapHandle h))
> 
> ```

(`attenuateAtKey` could be generalized to `attenuateByLens`)

---

<div class="post-metadata">

**Author:** ![kephas](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/kephas/32/3814_2.png) [@kephas](https://discourse.haskell.org/u/kephas)\
**Post date:** [September 5, 2026, 12:11pm UTC](https://discourse.haskell.org/t/bluefin-is-a-capability-system/14632/15 "2026-09-05T12:11:22Z")

</div>

> [@wolverian](#):
>
> I didn’t know about this authority/capability terminology.

I don’t remember ever seeing that distinction in the capability literature, and that comment contradicts itself at the end, saying exactly what I would say: `StartProcessAuth` is a broad capability, which is something we avoid designing as much as possible. `StartProcess` takes a path, which is basically an anti-pattern in capability-security, because that opens the possibility of confused deputy attacks.

Built on top of `StartProcessAuth`/`StartProcess`, a capability-based API would give you a `StartProcessDir` capability for `/` and you would get one for `/usr` and from there one for `/usr/bin` and from there for `/usr/bin/foo` and that’s the one you would pass to some code that needs to call `/usr/bin/foo`.

This is how you typically design capability APIs, but in this case, this might not make that much sense, because in the end, the process you start will have ambient authority, so the surface attack remains pretty wide. It would become actually meaningful if you used that API on a tree of wrappers that are sandboxed, even better if the API lets you give explicit capabilities to those processes in their sandbox.
