packages feed

kioku-cli-0.4.0.0: src/Kioku/Cli/Context.hs

-- | The memory space and principal the CLI acts as.
--
-- Kioku's core will not write anything without a 'MemoryAccessContext' — a record saying that
-- somebody already decided this caller may do this here. The CLI is a trusted in-process host
-- with no authentication boundary of its own, so it builds one through the deliberately
-- conspicuous 'assumeAuthorizedMemoryContext' rather than by consulting anything.
--
-- Two environment variables decide what it claims:
--
-- * @KIOKU_MEMORY_SPACE@ — which memory space the command reads and writes. It defaults to
--   'legacyMemorySpaceId' (@kioku_legacy@), which is where every row written before memory
--   spaces existed lives, so an unchanged CLI keeps operating on exactly the data it did before.
-- * @KIOKU_ACTOR@ — the principal writes are attributed to. It defaults to @kioku_cli@: the CLI
--   is genuinely the thing acting, and naming it plainly is better than borrowing an identity
--   from a directory the CLI does not talk to.
--
-- Both are validated, and a malformed value is a startup error rather than a silent fallback —
-- a typo in a memory space name must not quietly send writes somewhere else.
module Kioku.Cli.Context
  ( cliMemoryContext,
    cliMemoryActor,
    cliMemorySpace,
    cliContextProvider,
  )
where

import Data.Text (Text)
import Data.Text qualified as Text
import Kioku.Api.Access
  ( MemoryAccessContext,
    MemoryActor (..),
    MemoryContextProvider,
    MemorySpaceId,
    assumeAuthorizedContextProvider,
    assumeAuthorizedMemoryContext,
    legacyMemorySpaceId,
    mkMemorySpaceId,
    mkPrincipalRef,
  )
import System.Environment (lookupEnv)

-- | The context every CLI write runs under.
cliMemoryContext :: IO MemoryAccessContext
cliMemoryContext = assumeAuthorizedMemoryContext <$> cliMemorySpace <*> cliMemoryActor

-- | The provider a CLI-hosted background worker uses.
--
-- Unlike 'cliMemoryContext' this is not pinned to one space: a worker claims timers for whatever
-- space they were scheduled in, and refusing to serve them would strand the work. What the CLI
-- is asserting by using this is that a process holding its database credentials may act in any
-- space in that database — which is already true of anything with the connection string.
cliContextProvider :: (Applicative m) => IO (MemoryContextProvider m)
cliContextProvider = assumeAuthorizedContextProvider <$> cliMemoryActor

cliMemorySpace :: IO MemorySpaceId
cliMemorySpace =
  resolveEnv "KIOKU_MEMORY_SPACE" legacyMemorySpaceId mkMemorySpaceId

cliMemoryActor :: IO MemoryActor
cliMemoryActor =
  MemoryActor <$> resolveEnv "KIOKU_ACTOR" defaultActor mkPrincipalRef
  where
    defaultActor = either (error . Text.unpack) id (mkPrincipalRef "kioku_cli")

resolveEnv :: String -> a -> (Text -> Either Text a) -> IO a
resolveEnv name fallback parse = do
  raw <- lookupEnv name
  case raw of
    Nothing -> pure fallback
    Just value ->
      case parse (Text.pack value) of
        Right parsed -> pure parsed
        Left err -> ioError (userError (name <> ": " <> Text.unpack err))