{"id":17537522,"url":"https://github.com/zenhack/haskell-ocap","last_synced_at":"2025-04-23T21:07:54.261Z","repository":{"id":56874011,"uuid":"294333708","full_name":"zenhack/haskell-ocap","owner":"zenhack","description":"Haskell libraries for working with object capabilities.","archived":false,"fork":false,"pushed_at":"2020-11-25T11:01:13.000Z","size":18,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-23T21:07:41.998Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zenhack.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":"zenhack"}},"created_at":"2020-09-10T07:19:20.000Z","updated_at":"2023-07-14T23:16:06.000Z","dependencies_parsed_at":"2022-08-20T10:11:06.263Z","dependency_job_id":null,"html_url":"https://github.com/zenhack/haskell-ocap","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zenhack%2Fhaskell-ocap","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zenhack%2Fhaskell-ocap/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zenhack%2Fhaskell-ocap/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zenhack%2Fhaskell-ocap/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zenhack","download_url":"https://codeload.github.com/zenhack/haskell-ocap/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250514785,"owners_count":21443209,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-20T20:42:06.191Z","updated_at":"2025-04-23T21:07:54.223Z","avatar_url":"https://github.com/zenhack.png","language":"Haskell","readme":"Libraries for writing object-capability (ocap) safe code in Haskell.\n\n# Why\n\nIn theory, Haskell is a great language for running sandboxed code.\nBeing purely functional means that most code can't perform IO. While\nfeatures such as `unsafePerformIO` threaten this guarantee, there exists\na `SafeHaskell` language extension that can be used to wall off the\nunsound parts of the language, so that you can really believe the types.\n\nAnd indeed, some projects have had some success sandboxing Haskell code\nusing these mechanisms.\n\nHowever, when some access to IO is needed things get more difficult.\nThe standard library has the `IO` God-monad, which grants code running\nin it the ability to do anything.\n\nThere has been extensive exploration of finer-grained effect monads,\nthe idea being rather than grant code access to the all powerful `IO`,\nhave separate effect monads for filesystem access, network access, etc.\nThis is better in terms of avoiding unnecessary authority, but it lacks\ncomposability and is still too coarse-grained.\n\n## Composability\n\nMuch ink has been spilled on the problem of composing monads, and while\nuseful things have come of this, composing monads is clumsier than is\nideal. Furthermore, the separation of access we're interested in here\nhas nothing to do with monads: Ultimately, our effect monads for the\nfilesystem, networking etc, will be trivial wrappers around `IO` whose\nbind and return methods do nothing of interest. Monads fundamentally\nhave no relationship to the composition we're after -- it thus seems\nsilly to create composability problems for ourselves by introducing\nmultiple monads in the first place, if we can avoid it.\n\n## Granularity\n\nBeing able to grant only filesystem access, or only network access, is\nan improvement over having to grant arbitrary IO access, but it still\nleaves something to be desired. What if I only want to grant some code\naccess to a single directory, or a particular server?\n\n# The Solution\n\nThe solution is object capabilities -- rather than control the available\neffects through the type of the *monad*, we instead ban effects\ninvolving global variables -- including the implicit global variables\nprovided by the operating system, like file descriptor tables. Then,\neffects can only involve values (files, network hosts) that code\nactually has access to. We call these access-granting values\n*capabilities* (or sometimes *object capabilities* or just *ocaps*)\nA hypothetical network API might look like:\n\n```haskell\n\n-- | A handle for a remote host\ndata Host\n\n-- | A tcp port on a remote host\ndata TcpPort\n\n-- | Get a 'TcpPort' for a specific host. Does not establish a\n-- connection, just gets a value with more limited authority.\ngetHostPort :: Host -\u003e Word16 -\u003e TcpPort\n\n-- | An active connection to a remote host.\ndata Conn\n\n-- | Connect to a remote tcp port.\nconnect :: TcpPort -\u003e OCapIO Conn\n\n-- | Send/receive bytes on a connection:\nsend :: ByteString -\u003e Conn -\u003e OCapIO ()\nrecv :: Conn -\u003e Int -\u003e OCapIO ByteString\n\n-- ...\n```\n\nNotice: by itself, this API provides no way to access the network at\nall; just running in `OCapIO` isn't enough to do anything. If you\nwant to establish a connection, you need a `TcpPort`. To get one of\nthose, you can either accept one as an argument, or, if you have access\nto a `Host` you could derive a `TcpPort` using `getHostPort`. But that\njust begs the question, where does the `Host` come from? So `OCapIO`\nis like `IO` in that it is one monad that lets you do any kind of IO,\nbut by itself doesn't actually grant any authority -- you need to have\ncapabilities (like `Host`s and `Port`s) to the relevant resources for\nthat.\n\nThis is both much finer grained and much more composable than the effect\nmonad approach -- no fancy types, just plain old values.\n\nFor a longer introduction to object capabilities, see [This\npost](http://habitatchronicles.com/2017/05/).\n\n# So is it turtles all the way down?\n\nObviously, at some point you have to get a capability to _something_ in\norder to write useful programs -- otherwise you're just heating the CPU.\nTo that end, the `ocap-io` package provides an `IOKey` capability, that\ncan created in the `IO` monad and which can be used to run `IO` actions\ninside `OCapIO`. Finer grained access can then be built on top of this by\ncapturing this IO key inside of some opaque data type (or a lambda) and\nproviding a more limited API to consumers. See the documentation for\nthat package for more information.\n","funding_links":["https://github.com/sponsors/zenhack"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzenhack%2Fhaskell-ocap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzenhack%2Fhaskell-ocap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzenhack%2Fhaskell-ocap/lists"}