diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,6 @@
+0.9.3
+===
+* stdBox, toLineBox, fromLineBox added to Box.IO
+
+* concurrentlyLeft & concurrentlyRight exposed in Box.Queue
+
diff --git a/box.cabal b/box.cabal
--- a/box.cabal
+++ b/box.cabal
@@ -1,6 +1,6 @@
 cabal-version: 3.0
 name: box
-version: 0.9.2.1
+version: 0.9.3.0
 license: BSD-3-Clause
 license-file: LICENSE
 copyright: Tony Day (c) 2017
@@ -13,7 +13,10 @@
 description:
     This might be a profunctor effect system, but unlike all the others. See the project readme.org for usage and discussion: https://github.com/tonyday567/box#readme.org
 build-type: Simple
-tested-with: GHC ==8.10.7 || ==9.2.8 || ==9.4.7 || ==9.6.3 || ==9.8.1
+tested-with: GHC ==8.10.7 || ==9.4.7 || ==9.6.3 || ==9.8.1
+extra-doc-files:
+    ChangeLog.md
+    readme.org
 
 source-repository head
     type: git
@@ -101,7 +104,7 @@
         , semigroupoids  >=5.3 && <6.1
         , stm            >=2.5.1 && <2.6
         , text           >=1.2 && <2.2
-        , time           >=1.9 && <1.13
+        , time           >=1.10 && <1.13
     exposed-modules:
         Box
         Box.Box
diff --git a/readme.org b/readme.org
new file mode 100644
--- /dev/null
+++ b/readme.org
@@ -0,0 +1,367 @@
+#+TITLE: box
+
+[[https://hackage.haskell.org/package/box][file:https://img.shields.io/hackage/v/box.svg]] [[https://github.com/tonyday567/box/actions?query=workflow%3Ahaskell-ci][file:https://github.com/tonyday567/box/workflows/haskell-ci/badge.svg]]
+
+A profunctor effect system.
+
+#+begin_quote
+What is all this stuff around me; this stream of experiences that I seem to be having all the time? Throughout history there have been people who say it is all illusion. ~ S Blackmore
+#+end_quote
+
+* Usage
+
+#+begin_src haskell
+:set prompt "> "
+:set -XOverloadedStrings
+import Box
+import Prelude
+import Data.Function
+import Data.Bool
+#+end_src
+
+#+RESULTS:
+: >
+
+Standard IO echoing:
+
+#+begin_src haskell
+echoC = Committer (\s -> putStrLn ("echo: " <> s) >> pure True)
+echoE = Emitter (getLine & fmap (\x -> bool (Just x) Nothing (x =="quit")))
+glue echoC echoE
+#+end_src
+
+#+begin_src
+hello
+echo: hello
+echo
+echo: echo
+quit
+#+end_src
+
+Committing to a list:
+
+ #+begin_src haskell
+> toListM echoE
+hello
+echo
+quit
+["hello","echo"]
+ #+end_src
+
+Emitting from a list:
+
+#+begin_src haskell :results output
+> glue echoC <$|> witherE (\x -> bool (pure (Just x)) (pure Nothing) (x=="quit")) <$> (qList ["hello", "echo", "quit"])
+echo: hello
+echo: echo
+#+end_src
+
+* Library Design
+
+*** Resource Coinduction
+
+Haskell has an affinity with [[https://www.reddit.com/r/haskell/comments/j3kbge/comment/g7foelq/?utm_source=share&utm_medium=web2x&context=3][coinductive functions]]; functions should expose destructors and allow for infinite data.
+
+The key text, [[https://www.cs.kent.ac.uk/people/staff/dat/miranda/whyfp90.pdf][Why Functional Programming Matters]], details how producers and consumers can be separated by exploiting laziness, creating a speration of concern not available in other technologies. Utilising laziness, we can peel off (destruct) the next element of a list to be consumed without disturbing the pipeline of computations that is still to occur, for the cost of a thunk.
+
+So how do you apply this to resources and their effects? One answer is that you destruct a (potentially long-lived) resource simply by using it. For example, reading and writing lines to standard IO:
+
+ #+begin_src haskell :results output :exports both
+:t getLine
+:t putStrLn
+#+end_src
+
+#+RESULTS:
+: getLine :: IO String
+: putStrLn :: String -> IO ()
+
+These are the destructors that need to be transparently exposed if effects are to be good citizens in Haskell.
+
+*** What is a Box?
+
+A Box is simply the product of a consumer destructor and a producer destructor.
+
+#+begin_src haskell
+data Box m c e = Box
+  { committer :: Committer m c,
+    emitter :: Emitter m e
+  }
+#+end_src
+
+*** Committer
+
+The library denotes a consumer by wrapping a consumption destructor and calling it a Committer. Like much of base, there is failure hidden in the getLine example type. A better approach, for a consumer, is to signal whether consumption actually occurred.
+
+ #+begin_src haskell
+newtype Committer m a = Committer
+  { commit :: a -> m Bool
+  }
+ #+end_src
+
+You give a Committer an 'a', and the destructor tells you whether the consumption of the 'a' was successful or not. A standard output committer is then:
+
+#+begin_src haskell
+stdC :: Committer IO String
+stdC = Committer (\s -> putStrLn s >> pure True)
+#+end_src
+
+#+RESULTS:
+: <interactive>:19:1-4: warning: [GHC-63397] [-Wname-shadowing]
+:     This binding for ‘stdC’ shadows the existing binding
+:       defined at <interactive>:16:1
+
+A Committer is a contravariant functor, so contramap can be used to modify this:
+
+#+begin_src haskell
+import Data.Text as Text
+import Data.Functor.Contravariant
+
+echoC :: Committer IO Text
+echoC = contramap (Text.unpack . ("echo: "<>)) stdC
+#+end_src
+
+*** Emitter
+
+The library denotes a producer by wrapping a production destructor and calling it an Emitter.
+
+#+begin_src haskell
+newtype Emitter m a = Emitter
+  { emit :: m (Maybe a)
+  }
+#+end_src
+
+An emitter returns an 'a' on demand or not.
+
+#+begin_src haskell :results output
+stdE :: Emitter IO String
+stdE = Emitter (Just <$> getLine)
+#+end_src
+
+#+RESULTS:
+
+As a functor instance, an Emitter can be modified with fmap. Several library functions, such as witherE and filterE can also be used to stop emits or add effects.
+
+#+begin_src haskell :results output
+echoE :: Emitter IO Text
+echoE =
+  witherE (\x -> bool (pure (Just x)) (putStrLn "quitting" *> pure Nothing) (x == "quit"))
+    (fmap Text.pack stdE)
+#+end_src
+
+#+RESULTS:
+: <interactive>:52:1-5: warning: [GHC-63397] [-Wname-shadowing]
+:     This binding for ‘echoE’ shadows the existing binding
+:       defined at <interactive>:49:1
+
+*** Box duality
+
+A Box represents a duality in two ways:
+
+- As the consumer and producer sides of a resource. The complete interface to standard IO, for example, could be:
+
+#+begin_src haskell :results output
+stdIO :: Box IO String String
+stdIO = Box (Committer (\s -> putStrLn s >> pure True)) (Emitter (Just <$> getLine))
+#+end_src
+
+- As two ends of a computation.
+
+#+begin_quote
+This is how we can use a profunctor to glue together two categories ~ Milewski
+[[https://bartoszmilewski.com/2019/03/27/promonads-arrows-and-einstein-notation-for-profunctors/][Promonads, Arrows, and Einstein Notation for Profunctors]]
+#+end_quote
+
+~glue~ is the primitive with which we connect a Committer and Emitter.
+
+#+begin_src haskell
+> glue echoC echoE
+hello
+echo: hello
+echo
+echo: echo
+quit
+quitting
+#+end_src
+
+Effectively the same computation, for a Box, is:
+
+ #+begin_src haskell :results output
+fuse (pure . pure) stdIO
+ #+end_src
+
+*** Continuation
+
+As with many operators in the library, ~qList~ is actually a continuation:
+
+#+begin_src haskell :export both
+:t qList
+#+end_src
+
+#+RESULTS:
+: qList
+:   :: Control.Monad.Conc.Class.MonadConc m => [a] -> CoEmitter m a
+
+#+begin_src haskell
+type CoEmitter m a = Codensity m (Emitter m a)
+#+end_src
+
+Effectively being a newtype wrapper around:
+
+#+begin_src haskell
+forall x. (Emitter m a -> m x) -> m x
+#+end_src
+
+A good background on call-back style programming in Haskell is in the [[https://hackage.haskell.org/package/managed-1.0.10/docs/Control-Monad-Managed.html][managed]] library, which is a specialised version of Codensity.
+
+Codensity has an Applicative instance, and lends itself to applicative-style coding. To send a (queued) list to stdout, for example, you could say:
+
+#+begin_src haskell :export both
+:t glue <$> pure toStdout <*> qList ["a", "b", "c"]
+#+end_src
+
+#+RESULTS:
+: glue <$> pure toStdout <*> qList ["a", "b", "c"]
+:   :: Codensity IO (IO ())
+
+and then escape the continuation with:
+
+#+begin_src haskell :export both
+runCodensity (glue <$> pure toStdout <*> (qList ["a", "b", "c"])) id
+#+end_src
+
+#+RESULTS:
+: a
+: b
+: c
+
+This closes the continuation. The following code is equivalent:
+
+#+begin_src haskell :export both
+close $ glue <$> pure toStdout <*> qList ["a", "b", "c"]
+#+end_src
+
+#+RESULTS:
+: a
+: b
+: c
+
+#+begin_src haskell
+close $ glue toStdout <$> qList ["a", "b", "c"]
+#+end_src
+
+#+RESULTS:
+: a
+: b
+: c
+
+Given the ubiquity of this method, the library supplies two applicative style operators that combine application and closure.
+
+- =(<$|>)= fmap and close over a Codensity:
+
+#+begin_src haskell
+glue toStdout <$|> qList ["a", "b", "c"]
+#+end_src
+
+#+RESULTS:
+: a
+: b
+: c
+
+- =(<*|>)= Apply and close over Codensity
+
+#+begin_src haskell
+glue <$> pure toStdout <*|> qList ["a", "b", "c"]
+#+end_src
+
+#+RESULTS:
+: a
+: b
+: c
+
+* Explicit Continuation
+
+Yield-style streaming libraries are [[https://rubenpieters.github.io/assets/papers/JFP20-pipes.pdf][coroutines]], sum types that embed and mix continuation logic in with other stuff like effect decontruction. =box= sticks to a corner case of a product type representing a consumer and producer. The major drawback of eschewing coroutines is that continuations become explicit and difficult to hide. One example; taking the first n elements of an Emitter:
+
+#+begin_src haskell
+:t takeE
+takeE :: Monad m => Int -> Emitter m a -> Emitter (StateT Int m) a
+#+end_src
+
+A disappointing type. The state monad can not be hidden, the running count has to sit somewhere, and so different glueing functions are needed:
+
+#+begin_src haskell :results output
+-- | Connect a Stateful emitter to a (non-stateful) committer of the same type, supplying initial state.
+--
+-- >>> glueES 0 (showStdout) <$|> (takeE 2 <$> qList [1..3])
+-- 1
+-- 2
+glueES :: (Monad m) => s -> Committer m a -> Emitter (StateT s m) a -> m ()
+glueES s c e = flip evalStateT s $ glue (foist lift c) e
+#+end_src
+
+* Future directions
+
+The design and concepts contained within the box library is a hodge-podge, but an interesting mess, being at quite a busy confluence of recent developments.
+
+** Optics
+
+A Box is an adapter in the [[http://www.cs.ox.ac.uk/people/jeremy.gibbons/publications/poptics.pdf][language of optics]] and the relationship between a resource's committer and emitter could be modelled by other optics.
+
+** Categorical Profunctor
+
+The deprecation of Box.Functor awaits the development of [[https://github.com/haskell/core-libraries-committee/issues/91#issuecomment-1325337471][categorical functors]]. Similarly to Filterable the type of a Box could be something like =FunctorOf Op(Kleisli Maybe) (Kleisli Maybe) (->)=. Or it could be something like the SISO type in [[https://papers.ssrn.com/sol3/papers.cfm?abstract_id=4496714][Programming with Monoidal Profunctors and Semiarrows]].
+
+** Wider Types
+
+Alternatively, the types could be widened:
+
+#+begin_src haskell
+newtype Committer f a = Committer { commit :: a -> f () }
+
+instance Contravariant (Committer f) where
+  contramap f (Committer a) = Committer (a . f)
+
+newtype Emitter f a = Emitter { emit :: f a }
+
+instance (Functor f) => Functor (Emitter f) where
+  fmap f (Emitter a) = Emitter (fmap f a)
+
+data Box f g b a =
+  Box { committer :: Committer g b, emitter :: Emitter f a }
+
+instance (Functor f) => Functor (Box f g b) where
+  fmap f (Box c e) = Box c (fmap f e)
+
+instance (Functor f, Contravariant g) => Profunctor (Box f g) where
+  dimap f g (Box c e) = Box (contramap f c) (fmap g e)
+#+end_src
+
+.. with the existing computations recovered with:
+
+#+begin_src haskell
+type CommitterB m a = Committer (MaybeT m) a
+type EmitterB m a = Emitter (MaybeT m) a
+type BoxB m b a = Box (MaybeT m) (MaybeT m) b a
+#+end_src
+
+** Introduce a [[https://golem.ph.utexas.edu/category/2013/08/the_nucleus_of_a_profunctor_so.html][nucleus]]
+
+Alternative to both of these, the Monad constraint could be rethought. There are the ends of the computational pipeline, but there is also the gluing/fusion/middle bit.
+
+#+begin_src haskell
+connect :: (f a -> b) -> Committer g b -> Emitter f a -> g ()
+connect w c e = emit e & w & commit c
+
+glue :: Box f g (f a) a -> g ()
+glue (Box c e) = connect id c e
+
+nucleate ::
+  Functor f =>
+  (f a -> f b) ->
+  Committer g b ->
+  Emitter f a ->
+  f (g ())
+nucleate n c e = emit e & n & fmap (commit c)
+#+end_src
+
+This has the nice property that the closure is not hidden (as is usually the case for a Monad constraint) so that, for instance, fusion along longer chains becomes possible.
diff --git a/src/Box/IO.hs b/src/Box/IO.hs
--- a/src/Box/IO.hs
+++ b/src/Box/IO.hs
@@ -4,6 +4,7 @@
 module Box.IO
   ( fromStdin,
     toStdout,
+    stdBox,
     fromStdinN,
     toStdoutN,
     readStdin,
@@ -18,6 +19,8 @@
     fileEBS,
     fileCText,
     fileCBS,
+    toLineBox,
+    fromLineBox,
     logConsoleC,
     logConsoleE,
     pauser,
@@ -27,6 +30,7 @@
   )
 where
 
+import Box.Box
 import Box.Codensity
 import Box.Committer
 import Box.Connectors
@@ -43,6 +47,7 @@
 import Data.Sequence qualified as Seq
 import Data.String
 import Data.Text as Text hiding (null)
+import Data.Text.Encoding
 import Data.Text.IO as Text
 import System.IO as IO
 import Prelude
@@ -75,6 +80,10 @@
 toStdout :: Committer IO Text
 toStdout = Committer $ \a -> Text.putStrLn a >> pure True
 
+-- | A 'Box' to and from std, with an escape phrase.
+stdBox :: Text -> Box IO Text Text
+stdBox q = Box toStdout (takeUntilE (== q) fromStdin)
+
 -- | Finite console emitter
 --
 -- @
@@ -165,6 +174,14 @@
 -- | Commit ByteString to a file, as a line.
 fileCBS :: FilePath -> BufferMode -> IOMode -> CoCommitter IO ByteString
 fileCBS fp m b = fileC fp b m (handleC Char8.hPutStrLn)
+
+-- | Convert a 'Box' from ByteString to lines of Text.
+toLineBox :: Text -> Box IO ByteString ByteString -> CoBox IO Text Text
+toLineBox end (Box c e) = Box (contramap (encodeUtf8 . (<> end)) c) <$> evalEmitter [] (unlistE $ fmap (Text.lines . decodeUtf8Lenient) e)
+
+-- | Convert a 'Box' from lines of Text to ByteStrings.
+fromLineBox :: Text -> Box IO Text Text -> Box IO ByteString ByteString
+fromLineBox end (Box c e) = Box (contramap (Text.lines . decodeUtf8Lenient) (listC c)) (fmap (encodeUtf8 . (<> end)) e)
 
 -- | Commit to an IORef
 --
diff --git a/src/Box/Queue.hs b/src/Box/Queue.hs
--- a/src/Box/Queue.hs
+++ b/src/Box/Queue.hs
@@ -10,6 +10,8 @@
     fromActionWith,
     toBoxM,
     toBoxSTM,
+    concurrentlyLeft,
+    concurrentlyRight,
   )
 where
 
