diff --git a/attic.cabal b/attic.cabal
new file mode 100644
--- /dev/null
+++ b/attic.cabal
@@ -0,0 +1,70 @@
+cabal-version: 2.0
+
+-- This file has been generated from package.yaml by hpack version 0.37.0.
+--
+-- see: https://github.com/sol/hpack
+
+name:           attic
+version:        0.1.0.0
+synopsis:       Haskell bindings for Nix attic cache
+description:    Haskell types and functions for working with NIx attic cache servers
+category:       System
+homepage:       https://github.com/juspay/vira
+author:         Sridhar Ratnakumar
+maintainer:     srid@srid.ca
+copyright:      2025 Juspay
+license:        MIT
+build-type:     Simple
+
+library
+  exposed-modules:
+      Attic
+  other-modules:
+      Paths_attic
+  autogen-modules:
+      Paths_attic
+  hs-source-dirs:
+      src
+  default-extensions:
+      DataKinds
+      DeriveDataTypeable
+      DeriveGeneric
+      DeriveTraversable
+      DerivingStrategies
+      DerivingVia
+      ExplicitForAll
+      FlexibleContexts
+      FlexibleInstances
+      GeneralizedNewtypeDeriving
+      ImportQualifiedPost
+      LambdaCase
+      MultiParamTypeClasses
+      MultiWayIf
+      NamedFieldPuns
+      NoStarIsType
+      NumericUnderscores
+      OverloadedStrings
+      ScopedTypeVariables
+      StrictData
+      TypeApplications
+      TypeFamilies
+      TypeOperators
+      TypeSynonymInstances
+      ViewPatterns
+  ghc-options: -Wall -optP-Wno-nonportable-include-path -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-deriving-strategies -Wunused-foralls -fprint-explicit-foralls -fprint-explicit-kinds
+  build-depends:
+      aeson
+    , base ==4.*
+    , http-api-data
+    , process
+    , relude >=1.0
+    , safecopy
+    , servant
+    , template-haskell
+    , text
+    , which
+  mixins:
+      base hiding (Prelude)
+    , relude (Relude as Prelude, Relude.Container.One)
+    , relude 
+  default-language: GHC2021
diff --git a/src/Attic.hs b/src/Attic.hs
new file mode 100644
--- /dev/null
+++ b/src/Attic.hs
@@ -0,0 +1,74 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE TemplateHaskell #-}
+
+-- | Working with [attic](https://github.com/zhaofengli/attic) cache servers
+module Attic where
+
+import Data.Aeson (FromJSON, ToJSON)
+import Data.SafeCopy
+import Servant.API (FromHttpApiData, ToHttpApiData)
+import System.Process (CreateProcess, proc)
+import System.Which (staticWhich)
+import Web.FormUrlEncoded (FromForm)
+
+-- | Reference to a self-hosted attic server
+data AtticServer = AtticServer
+  { serverName :: Text
+  , serverUrl :: Text
+  }
+  deriving stock (Eq, Show, Generic)
+  deriving anyclass (FromForm, ToJSON, FromJSON)
+
+-- | An attic login token
+newtype AtticToken = AtticToken {unAtticToken :: Text}
+  deriving stock (Eq, Show)
+  deriving newtype
+    ( IsString
+    , ToString
+    , ToHttpApiData
+    , FromHttpApiData
+    , ToJSON
+    , FromJSON
+    )
+
+-- | An attic cache name
+newtype AtticCache = AtticCache {unAtticCache :: Text}
+  deriving stock (Eq, Show)
+  deriving newtype
+    ( IsString
+    , ToString
+    , ToText
+    , ToHttpApiData
+    , FromHttpApiData
+    , ToJSON
+    , FromJSON
+    )
+
+$(deriveSafeCopy 0 'base ''AtticServer)
+$(deriveSafeCopy 0 'base ''AtticCache)
+$(deriveSafeCopy 0 'base ''AtticToken)
+
+{- | Path to the `attic` executable
+
+This should be available in the PATH, thanks to Nix and `which` library.
+-}
+atticBin :: FilePath
+atticBin = $(staticWhich "attic")
+
+{- | Push the given path to the attic server cache
+
+NOTE: `atticLoginProcess` should be run before this to set the access token
+-}
+atticPushProcess :: AtticServer -> AtticCache -> FilePath -> CreateProcess
+atticPushProcess AtticServer {serverName} cacheName path =
+  proc atticBin ["push", toString serverName <> ":" <> toString cacheName, path]
+
+{- | Saves the access token for the attic server
+
+Run this process before other attic processes.
+
+TODO: Remove after https://github.com/zhaofengli/attic/issues/243 is resolved to provide stateless access
+-}
+atticLoginProcess :: AtticServer -> AtticToken -> CreateProcess
+atticLoginProcess AtticServer {serverName, serverUrl} token =
+  proc atticBin ["login", toString serverName, toString serverUrl, toString token]
