diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 1d00ee56f4..2a3379547d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -214,6 +214,8 @@ set(src_py_support_files
 set(src_capnp_specs_rel
   nupic/proto/ArrayProto.capnp
   nupic/proto/BitHistory.capnp
+  nupic/proto/Cell.capnp
+  nupic/proto/Cells4.capnp
   nupic/proto/ClaClassifier.capnp
   nupic/proto/ConnectionsProto.capnp
   nupic/proto/LinkProto.capnp
@@ -222,6 +224,8 @@ set(src_capnp_specs_rel
   nupic/proto/PyRegionProto.capnp
   nupic/proto/RandomProto.capnp
   nupic/proto/RegionProto.capnp
+  nupic/proto/Segment.capnp
+  nupic/proto/SegmentUpdate.capnp
   nupic/proto/SparseBinaryMatrixProto.capnp
   nupic/proto/SparseMatrixProto.capnp
   nupic/proto/SpatialPoolerProto.capnp
diff --git a/src/nupic/algorithms/Cell.cpp b/src/nupic/algorithms/Cell.cpp
index 29f324de4d..cda1e1a0bb 100644
--- a/src/nupic/algorithms/Cell.cpp
+++ b/src/nupic/algorithms/Cell.cpp
@@ -20,6 +20,7 @@
  * ---------------------------------------------------------------------
  */
 
+#include <nupic/proto/Cell.capnp.h>
 #include <nupic/algorithms/Cell.hpp>
 
 using namespace nupic::algorithms::Cells4;
@@ -121,6 +122,34 @@ void Cell::updateDutyCycle(UInt iterations)
   }
 }
 
+//-----------------------------------------------------------------------------
+void Cell::write(CellProto::Builder& proto) const
+{
+  auto segmentsProto = proto.initSegments(_segments.size());
+  for (UInt i = 0; i < _segments.size(); ++i)
+  {
+    auto segProto = segmentsProto[i];
+    _segments[i].write(segProto);
+  }
+}
+
+//-----------------------------------------------------------------------------
+void Cell::read(CellProto::Reader& proto)
+{
+  auto segmentsProto = proto.getSegments();
+  _segments.resize(segmentsProto.size());
+  _freeSegments.resize(0);
+  for (UInt i = 0; i < segmentsProto.size(); ++i)
+  {
+    auto segProto = segmentsProto[i];
+    _segments[i].read(segProto);
+    if (_segments[i].empty())
+    {
+      _freeSegments.push_back(i);
+    }
+  }
+}
+
 //-----------------------------------------------------------------------------
 void Cell::save(std::ostream& outStream) const
 {
diff --git a/src/nupic/algorithms/Cell.hpp b/src/nupic/algorithms/Cell.hpp
index 66e96becdf..299ad40ef3 100644
--- a/src/nupic/algorithms/Cell.hpp
+++ b/src/nupic/algorithms/Cell.hpp
@@ -24,6 +24,8 @@
 #define NTA_CELL_HPP
 
 #include <vector>
+#include <nupic/proto/Cell.capnp.h>
+#include <nupic/types/Serializable.hpp>
 #include <nupic/types/Types.hpp>
 #include <nupic/algorithms/Segment.hpp>
 
@@ -44,7 +46,7 @@ namespace nupic {
        * mismatches in unit testing when comparing the Python TP to the C++ down to the
        * segment level.
        */
-      class Cell
+      class Cell : Serializable<CellProto>
       {
       private:
         std::vector< Segment >   _segments;     // both 'active' and 'inactive' segments
@@ -252,6 +254,14 @@ namespace nupic {
           return buff.str().size();
         }
 
+        //----------------------------------------------------------------------
+        using Serializable::write;
+        virtual void write(CellProto::Builder& proto) const override;
+
+        //----------------------------------------------------------------------
+        using Serializable::read;
+        virtual void read(CellProto::Reader& proto) override;
+
         //----------------------------------------------------------------------
         void save(std::ostream& outStream) const;
 
diff --git a/src/nupic/algorithms/Cells4.cpp b/src/nupic/algorithms/Cells4.cpp
index ac66a3ed22..7656349825 100644
--- a/src/nupic/algorithms/Cells4.cpp
+++ b/src/nupic/algorithms/Cells4.cpp
@@ -35,14 +35,14 @@
 #include <assert.h>
 #include <cstring>
 #include <map>
-#include <nupic/math/ArrayAlgo.hpp> // is_in
-#include <nupic/math/StlIo.hpp> // binary_save
+#include <nupic/algorithms/Cell.hpp>
 #include <nupic/algorithms/Cells4.hpp>
 #include <nupic/algorithms/SegmentUpdate.hpp>
-#include <nupic/algorithms/Cell.hpp>
+#include <nupic/math/ArrayAlgo.hpp> // is_in
+#include <nupic/math/StlIo.hpp> // binary_save
 #include <nupic/os/FStream.hpp>
-
 #include <nupic/os/Timer.hpp>
+#include <nupic/proto/Cells4.capnp.h>
 
 using namespace nupic::algorithms::Cells4;
 
@@ -1931,6 +1931,148 @@ void Cells4::reset()
   //}
 }
 
+
+//--------------------------------------------------------------------------------
+void Cells4::write(Cells4Proto::Builder& proto) const
+{
+  proto.setVersion(version());
+  proto.setOwnsMemory(_ownsMemory);
+  auto randomProto = proto.initRng();
+  _rng.write(randomProto);
+  proto.setNColumns(_nColumns);
+  proto.setNCellsPerCol(_nCellsPerCol);
+  proto.setActivationThreshold(_activationThreshold);
+  proto.setMinThreshold(_minThreshold);
+  proto.setNewSynapseCount(_newSynapseCount);
+  proto.setNIterations(_nIterations);
+  proto.setNLrnIterations(_nLrnIterations);
+  proto.setSegUpdateValidDuration(_segUpdateValidDuration);
+  proto.setInitSegFreq(_initSegFreq);
+  proto.setPermInitial(_permInitial);
+  proto.setPermConnected(_permConnected);
+  proto.setPermMax(_permMax);
+  proto.setPermDec(_permDec);
+  proto.setPermInc(_permInc);
+  proto.setGlobalDecay(_globalDecay);
+  proto.setDoPooling(_doPooling);
+  proto.setPamLength(_pamLength);
+  proto.setMaxInfBacktrack(_maxInfBacktrack);
+  proto.setMaxLrnBacktrack(_maxLrnBacktrack);
+  proto.setMaxSeqLength(_maxSeqLength);
+  proto.setLearnedSeqLength(_learnedSeqLength);
+  proto.setAvgLearnedSeqLength(_avgLearnedSeqLength);
+  proto.setMaxAge(_maxAge);
+  proto.setVerbosity(_verbosity);
+  proto.setMaxSegmentsPerCell(_maxSegmentsPerCell);
+  proto.setMaxSynapsesPerSegment(_maxSynapsesPerSegment);
+  proto.setCheckSynapseConsistency(_checkSynapseConsistency);
+  proto.setResetCalled(_resetCalled);
+  proto.setAvgInputDensity(_avgInputDensity);
+  proto.setPamCounter(_pamCounter);
+
+  auto learnActiveStateTProto = proto.initLearnActiveStateT();
+  _learnActiveStateT.write(learnActiveStateTProto);
+  auto learnActiveStateT1Proto = proto.initLearnActiveStateT1();
+  _learnActiveStateT1.write(learnActiveStateT1Proto);
+  auto learnPredictedStateTProto = proto.initLearnPredictedStateT();
+  _learnPredictedStateT.write(learnPredictedStateTProto);
+  auto learnPredictedStateT1Proto = proto.initLearnPredictedStateT1();
+  _learnPredictedStateT1.write(learnPredictedStateT1Proto);
+
+  auto cellListProto = proto.initCells(_nCells);
+  for (UInt i = 0; i < _nCells; ++i)
+  {
+    auto cellProto = cellListProto[i];
+    _cells[i].write(cellProto);
+  }
+
+  auto segmentUpdatesListProto = proto.initSegmentUpdates(
+      _segmentUpdates.size());
+  for (UInt i = 0; i < _segmentUpdates.size(); ++i)
+  {
+    auto segmentUpdateProto = segmentUpdatesListProto[i];
+    _segmentUpdates[i].write(segmentUpdateProto);
+  }
+}
+
+
+//--------------------------------------------------------------------------------
+void Cells4::read(Cells4Proto::Reader& proto)
+{
+  NTA_CHECK(proto.getVersion() == 2);
+  _ownsMemory = proto.getOwnsMemory();
+  auto randomProto = proto.getRng();
+  _rng.read(randomProto);
+  _nColumns = proto.getNColumns();
+  _nCellsPerCol = proto.getNCellsPerCol();
+  _activationThreshold = proto.getActivationThreshold();
+  _minThreshold = proto.getMinThreshold();
+  _newSynapseCount = proto.getNewSynapseCount();
+  _nIterations = proto.getNIterations();
+  _nLrnIterations = proto.getNLrnIterations();
+  _segUpdateValidDuration = proto.getSegUpdateValidDuration();
+  _initSegFreq = proto.getInitSegFreq();
+  _permInitial = proto.getPermInitial();
+  _permConnected = proto.getPermConnected();
+  _permMax = proto.getPermMax();
+  _permDec = proto.getPermDec();
+  _permInc = proto.getPermInc();
+  _globalDecay = proto.getGlobalDecay();
+  _doPooling = proto.getDoPooling();
+  _pamLength = proto.getPamLength();
+  _maxInfBacktrack = proto.getMaxInfBacktrack();
+  _maxLrnBacktrack = proto.getMaxLrnBacktrack();
+  _maxSeqLength = proto.getMaxSeqLength();
+  _learnedSeqLength = proto.getLearnedSeqLength();
+  _avgLearnedSeqLength = proto.getAvgLearnedSeqLength();
+  _maxAge = proto.getMaxAge();
+  _verbosity = proto.getVerbosity();
+  _maxSegmentsPerCell = proto.getMaxSegmentsPerCell();
+  _maxSynapsesPerSegment = proto.getMaxSynapsesPerSegment();
+  _checkSynapseConsistency = proto.getCheckSynapseConsistency();
+
+  _resetCalled = proto.getResetCalled();
+
+  _avgInputDensity = proto.getAvgInputDensity();
+  _pamCounter = proto.getPamCounter();
+
+  auto learnActiveStateTProto = proto.getLearnActiveStateT();
+  _learnActiveStateT.read(learnActiveStateTProto);
+  auto learnActiveStateT1Proto = proto.getLearnActiveStateT1();
+  _learnActiveStateT1.read(learnActiveStateT1Proto);
+  auto learnPredictedStateTProto = proto.getLearnPredictedStateT();
+  _learnPredictedStateT.read(learnPredictedStateTProto);
+  auto learnPredictedStateT1Proto = proto.getLearnPredictedStateT1();
+  _learnPredictedStateT1.read(learnPredictedStateT1Proto);
+
+  auto cellListProto = proto.getCells();
+  _nCells = cellListProto.size();
+  _cells.resize(_nCells);
+  for (UInt i = 0; i < cellListProto.size(); ++i)
+  {
+    auto cellProto = cellListProto[i];
+    _cells[i].read(cellProto);
+  }
+
+  auto segmentUpdatesListProto = proto.getSegmentUpdates();
+  _segmentUpdates.clear();
+  _segmentUpdates.resize(segmentUpdatesListProto.size());
+  for (UInt i = 0; i < segmentUpdatesListProto.size(); ++i)
+  {
+    auto segmentUpdateProto = segmentUpdatesListProto[i];
+    _segmentUpdates[i].read(segmentUpdateProto);
+  }
+
+  rebuildOutSynapses();
+  if (_checkSynapseConsistency || (_nCells * _maxSegmentsPerCell < 100000))
+  {
+    NTA_CHECK(invariants(true));
+  }
+
+  _version = VERSION;
+}
+
+
 //--------------------------------------------------------------------------------
 void Cells4::save(std::ostream& outStream) const
 {
diff --git a/src/nupic/algorithms/Cells4.hpp b/src/nupic/algorithms/Cells4.hpp
index 2762a03fff..5ae8efc8f1 100644
--- a/src/nupic/algorithms/Cells4.hpp
+++ b/src/nupic/algorithms/Cells4.hpp
@@ -26,9 +26,11 @@
 #include <ostream>
 #include <sstream>
 #include <fstream>
-#include <nupic/types/Types.hpp>
 #include <nupic/algorithms/Segment.hpp>
 #include <nupic/algorithms/OutSynapse.hpp>
+#include <nupic/proto/Cells4.capnp.h>
+#include <nupic/types/Serializable.hpp>
+#include <nupic/types/Types.hpp>
 #include <queue>
 #include <cstring>
 
@@ -262,7 +264,7 @@ namespace nupic {
         CBasicActivity<It> _seg;
       };
 
-      class Cells4
+      class Cells4 : public Serializable<Cells4Proto>
       {
       public:
 
@@ -1073,6 +1075,20 @@ namespace nupic {
           return tmp.str().size();
         }
 
+        //----------------------------------------------------------------------
+        /**
+         * Write the state to a proto or file
+         */
+        using Serializable::write;
+        virtual void write(Cells4Proto::Builder& proto) const override;
+
+        //----------------------------------------------------------------------
+        /**
+         * Read the state into a proto or file
+         */
+        using Serializable::read;
+        virtual void read(Cells4Proto::Reader& proto) override;
+
         //----------------------------------------------------------------------
         /**
          * Save the state to the given file
diff --git a/src/nupic/algorithms/Segment.hpp b/src/nupic/algorithms/Segment.hpp
index bf3bbfe7a2..441a03172d 100644
--- a/src/nupic/algorithms/Segment.hpp
+++ b/src/nupic/algorithms/Segment.hpp
@@ -33,6 +33,8 @@
 
 #include <nupic/math/ArrayAlgo.hpp> // is_sorted
 #include <nupic/math/StlIo.hpp> // binary_save
+#include <nupic/proto/Segment.capnp.h>
+#include <nupic/types/Serializable.hpp>
 #include <nupic/algorithms/InSynapse.hpp>
 
 
@@ -88,7 +90,7 @@ namespace nupic {
       /**
        * Encapsulate the arrays used to maintain per-cell state.
        */
-      class CState
+      class CState : Serializable<CStateProto>
       {
       public:
         static const UInt VERSION = 1;
@@ -165,6 +167,29 @@ namespace nupic {
           outStream << std::endl
                     << "end" << std::endl;
         }
+        using Serializable::write;
+        virtual void write(CStateProto::Builder& proto) const override
+        {
+          proto.setVersion(VERSION);
+          proto.setFMemoryAllocatedByPython(_fMemoryAllocatedByPython);
+          auto pDataProto = proto.initPData(_nCells);
+          for (UInt i = 0; i < _nCells; ++i)
+          {
+            pDataProto[i] = _pData[i];
+          }
+        }
+        using Serializable::read;
+        virtual void read(CStateProto::Reader& proto) override
+        {
+          NTA_CHECK(proto.getVersion() == 1);
+          _fMemoryAllocatedByPython = proto.getFMemoryAllocatedByPython();
+          auto pDataProto = proto.getPData();
+          _nCells = pDataProto.size();
+          for (UInt i = 0; i < _nCells; ++i)
+          {
+            _pData[i] = pDataProto[i];
+          }
+        }
         void load(std::istream& inStream)
         {
           UInt version;
@@ -271,6 +296,27 @@ namespace nupic {
           }
           outStream << "end" << std::endl;
         }
+        void write(CStateProto::Builder& proto) const override
+        {
+          CState::write(proto);
+          proto.setCountOn(_countOn);
+          auto cellsOnProto = proto.initCellsOn(_cellsOn.size());
+          for (UInt i = 0; i < _cellsOn.size(); ++i)
+          {
+            cellsOnProto.set(i, _cellsOn[i]);
+          }
+        }
+        void read(CStateProto::Reader& proto) override
+        {
+          CState::read(proto);
+          _countOn = proto.getCountOn();
+          auto cellsOnProto = proto.getCellsOn();
+          _cellsOn.resize(cellsOnProto.size());
+          for (UInt i = 0; i < cellsOnProto.size(); ++i)
+          {
+            _cellsOn[i] = cellsOnProto[i];
+          }
+        }
         void load(std::istream& inStream)
         {
           UInt version;
@@ -334,7 +380,7 @@ namespace nupic {
 
 
       //-----------------------------------------------------------------------
-      class Segment
+      class Segment : Serializable<SegmentProto>
       {
       public:
         typedef std::vector< InSynapse > InSynapses;
@@ -812,6 +858,48 @@ namespace nupic {
           return buff.str().size();
         }
 
+        //----------------------------------------------------------------------
+        using Serializable::write;
+        void write(SegmentProto::Builder& proto) const override
+        {
+          NTA_ASSERT(invariants());
+          proto.setSeqSegFlag(_seqSegFlag);
+          proto.setFrequency(_frequency);
+          proto.setNConnected(_nConnected);
+          proto.setTotalActivations(_totalActivations);
+          proto.setPositiveActivations(_positiveActivations);
+          proto.setLastActiveIteration(_lastActiveIteration);
+          proto.setLastPosDutyCycle(_lastPosDutyCycle);
+          proto.setLastPosDutyCycleIteration(_lastPosDutyCycleIteration);
+          auto synapsesProto = proto.initSynapses(size());
+          for (UInt i = 0; i < size(); ++i)
+          {
+            auto inSynapseProto = synapsesProto[i];
+            inSynapseProto.setSrcCellIdx(_synapses[i].srcCellIdx());
+            inSynapseProto.setPermanence(_synapses[i].permanence());
+          }
+        }
+
+        //----------------------------------------------------------------------
+        using Serializable::read;
+        void read(SegmentProto::Reader& proto) override
+        {
+          _seqSegFlag = proto.getSeqSegFlag();
+          _frequency = proto.getFrequency();
+          _nConnected = proto.getNConnected();
+          _totalActivations = proto.getTotalActivations();
+          _positiveActivations = proto.getPositiveActivations();
+          _lastActiveIteration = proto.getLastActiveIteration();
+          _lastPosDutyCycle = proto.getLastPosDutyCycle();
+          _lastPosDutyCycleIteration = proto.getLastPosDutyCycleIteration();
+          _synapses.clear();
+          for (auto inSynapseProto : proto.getSynapses())
+          {
+            _synapses.emplace_back(inSynapseProto.getSrcCellIdx(),
+                                   inSynapseProto.getPermanence());
+          }
+        }
+
         //----------------------------------------------------------------------
         inline void save(std::ostream& outStream) const
         {
diff --git a/src/nupic/algorithms/SegmentUpdate.hpp b/src/nupic/algorithms/SegmentUpdate.hpp
index 22acdfb0c3..a3711a3eb4 100644
--- a/src/nupic/algorithms/SegmentUpdate.hpp
+++ b/src/nupic/algorithms/SegmentUpdate.hpp
@@ -23,6 +23,8 @@
 #ifndef NTA_SEGMENTUPDATE_HPP
 #define NTA_SEGMENTUPDATE_HPP
 
+#include <nupic/proto/SegmentUpdate.capnp.h>
+#include <nupic/types/Serializable.hpp>
 #include <nupic/types/Types.hpp>
 #include <vector>
 using namespace nupic;
@@ -43,7 +45,7 @@ namespace nupic {
        * than the iteration they were created in. SegmentUpdates have a timeStamp,
        * and they are discarded without being applied if they become 'stale'.
        */
-      class SegmentUpdate
+      class SegmentUpdate : Serializable<SegmentUpdateProto>
       {
       public:
         typedef std::vector<UInt>::const_iterator const_iterator;
@@ -110,6 +112,41 @@ namespace nupic {
           */
          bool invariants(Cells4* cells =nullptr) const;
 
+         //---------------------------------------------------------------------
+         using Serializable::write;
+         void write(SegmentUpdateProto::Builder& proto) const override
+         {
+           proto.setSequenceSegment(_sequenceSegment);
+           proto.setCellIdx(_cellIdx);
+           proto.setSegIdx(_segIdx);
+           proto.setTimestamp(_timeStamp);
+           auto synapsesProto = proto.initSynapses(_synapses.size());
+           for (UInt i = 0; i < _synapses.size(); ++i)
+           {
+             synapsesProto.set(i, _synapses[i]);
+           }
+           proto.setPhase1Flag(_phase1Flag);
+           proto.setWeaklyPredicting(_weaklyPredicting);
+         }
+
+         //---------------------------------------------------------------------
+         using Serializable::read;
+         void read(SegmentUpdateProto::Reader& proto) override
+         {
+           _sequenceSegment = proto.getSequenceSegment();
+           _cellIdx = proto.getCellIdx();
+           _segIdx = proto.getSegIdx();
+           _timeStamp = proto.getTimestamp();
+           auto synapsesProto = proto.getSynapses();
+           _synapses.resize(synapsesProto.size());
+           for (UInt i = 0; i < synapsesProto.size(); ++i)
+           {
+             _synapses[i] = synapsesProto[i];
+           }
+           _phase1Flag = proto.getPhase1Flag();
+           _weaklyPredicting = proto.getWeaklyPredicting();
+         }
+
          //---------------------------------------------------------------------
          void save(std::ostream& outStream) const
          {
diff --git a/src/nupic/proto/Cell.capnp b/src/nupic/proto/Cell.capnp
new file mode 100644
index 0000000000..f95a9f9f63
--- /dev/null
+++ b/src/nupic/proto/Cell.capnp
@@ -0,0 +1,7 @@
+@0xcf9698b0f93fc8e4;
+
+using import "/nupic/proto/Segment.capnp".SegmentProto;
+
+struct CellProto {
+  segments @0 :List(SegmentProto);
+}
diff --git a/src/nupic/proto/Cells4.capnp b/src/nupic/proto/Cells4.capnp
new file mode 100644
index 0000000000..045b3f7fd1
--- /dev/null
+++ b/src/nupic/proto/Cells4.capnp
@@ -0,0 +1,55 @@
+@0xd5c4908fa0384eda;
+
+using import "/nupic/proto/Cell.capnp".CellProto;
+using import "/nupic/proto/RandomProto.capnp".RandomProto;
+using import "/nupic/proto/Segment.capnp".CStateProto;
+using import "/nupic/proto/SegmentUpdate.capnp".SegmentUpdateProto;
+
+# Next ID: 39
+struct Cells4Proto {
+  version @0 :UInt16;
+  ownsMemory @1 :Bool;
+  rng @2 :RandomProto;
+  nColumns @3 :UInt32;
+  nCellsPerCol @4 :UInt32;
+  activationThreshold @5 :UInt32;
+  minThreshold @6 :UInt32;
+  newSynapseCount @7 :UInt32;
+  nIterations @8 :UInt32;
+  nLrnIterations @9 :UInt32;
+  segUpdateValidDuration @10 :UInt32;
+  initSegFreq @11 :Float32;
+  permInitial @12 :Float32;
+  permConnected @13 :Float32;
+  permMax @14 :Float32;
+  permDec @15 :Float32;
+  permInc @16 :Float32;
+  globalDecay @17 :Float32;
+  doPooling @18 :Bool;
+  pamLength @19 :UInt32;
+  maxInfBacktrack @20 :UInt32;
+  maxLrnBacktrack @21 :UInt32;
+  maxSeqLength @22 :UInt32;
+  learnedSeqLength @23 :UInt32;
+  avgLearnedSeqLength @24 :Float32;
+  maxAge @25 :UInt32;
+  verbosity @26 :UInt8;
+  maxSegmentsPerCell @27 :UInt32;
+  maxSynapsesPerSegment @28 :UInt32;
+  checkSynapseConsistency @29 :Bool;
+
+  # Internal variables
+  resetCalled @30 :Bool;
+
+  avgInputDensity @31 :Float32;
+  pamCounter @32 :UInt32;
+
+  # The various inference and learning states
+  learnActiveStateT @33 :CStateProto;
+  learnActiveStateT1 @34 :CStateProto;
+  learnPredictedStateT @35 :CStateProto;
+  learnPredictedStateT1 @36 :CStateProto;
+
+  cells @37 :List(CellProto);
+  segmentUpdates @38 :List(SegmentUpdateProto);
+}
diff --git a/src/nupic/proto/Segment.capnp b/src/nupic/proto/Segment.capnp
new file mode 100644
index 0000000000..b6d5720662
--- /dev/null
+++ b/src/nupic/proto/Segment.capnp
@@ -0,0 +1,33 @@
+@0xf2eaadec04697984;
+
+# Next ID: 2
+struct InSynapseProto {
+  srcCellIdx @0 :UInt32;
+  permanence @1 :Float32;
+}
+
+# Next ID: 9
+struct SegmentProto {
+  seqSegFlag @0 :Bool;
+  frequency @1 :Float32;
+  nConnected @2 :UInt32;
+  totalActivations @3 :UInt32;
+  positiveActivations @4 :UInt32;
+  lastActiveIteration @5 :UInt32;
+  lastPosDutyCycle @6 :Float32;
+  lastPosDutyCycleIteration @7 :UInt32;
+  synapses @8 :List(InSynapseProto);
+}
+
+# Next ID: 5
+struct CStateProto {
+  version @0 :UInt16;
+  fMemoryAllocatedByPython @1 :Bool;
+  # length of list is stored as nCells in class
+  pData @2 :Data;
+
+  # CStateIndexed additional variables. If not using CStateIndexed, these
+  # values should be ignored.
+  countOn @3 :UInt32;
+  cellsOn @4 :List(UInt32);
+}
diff --git a/src/nupic/proto/SegmentUpdate.capnp b/src/nupic/proto/SegmentUpdate.capnp
new file mode 100644
index 0000000000..3a1ae7c5dc
--- /dev/null
+++ b/src/nupic/proto/SegmentUpdate.capnp
@@ -0,0 +1,12 @@
+@0xe7a17ea776969175;
+
+# Next ID: 7
+struct SegmentUpdateProto {
+  sequenceSegment @0 :Bool;
+  cellIdx @1 :UInt32;
+  segIdx @2 :UInt32;
+  timestamp @3 :UInt32;
+  synapses @4 :List(UInt32);
+  phase1Flag @5 :Bool;
+  weaklyPredicting @6 :Bool;
+}
diff --git a/src/test/unit/algorithms/Cells4Test.cpp b/src/test/unit/algorithms/Cells4Test.cpp
index 5cd2b0c93d..89e9ffee39 100644
--- a/src/test/unit/algorithms/Cells4Test.cpp
+++ b/src/test/unit/algorithms/Cells4Test.cpp
@@ -27,11 +27,15 @@
 #include <set>
 #include <vector>
 
+#include <capnp/message.h>
+#include <capnp/serialize.h>
+#include <kj/std/iostream.h>
 #include <gtest/gtest.h>
 
 #include <nupic/algorithms/Cells4.hpp>
 #include <nupic/algorithms/Segment.hpp>
 #include <nupic/math/ArrayAlgo.hpp> // is_in
+#include <nupic/proto/Cells4.capnp.h>
 
 
 using namespace nupic::algorithms::Cells4;
@@ -78,6 +82,44 @@ std::vector<UInt> _getOrderedSynapseIndexesForSrcCells(const Segment& segment,
 }
 
 
+TEST(Cells4Test, capnpSerialization)
+{
+  Cells4 cells(
+      10, 2, 1, 1, 1, 1, 0.5, 0.8, 1, 0.1, 0.1, 0, false, -1, true, false);
+  std::vector<Real> input(10, 0.0);
+  input[1] = 1.0;
+  input[4] = 1.0;
+  input[5] = 1.0;
+  input[9] = 1.0;
+  std::vector<Real> output(10*2);
+  cells.compute(&input.front(), &output.front(), true, true);
+
+  Cells4 secondCells(
+      10, 2, 1, 1, 1, 1, 0.5, 0.8, 1, 0.1, 0.1, 0, false, -1, true, false);
+  {
+    capnp::MallocMessageBuilder message1;
+    Cells4Proto::Builder cells4Builder = message1.initRoot<Cells4Proto>();
+    cells.write(cells4Builder);
+    std::stringstream ss;
+    kj::std::StdOutputStream out(ss);
+    capnp::writeMessage(out, message1);
+
+    kj::std::StdInputStream in(ss);
+    capnp::InputStreamMessageReader message2(in);
+    Cells4Proto::Reader cells4Reader = message2.getRoot<Cells4Proto>();
+    secondCells.read(cells4Reader);
+  }
+
+  std::vector<Real> secondOutput(10*2);
+  cells.compute(&input.front(), &output.front(), true, true);
+  secondCells.compute(&input.front(), &secondOutput.front(), true, true);
+  for (UInt i = 0; i < 10; ++i)
+  {
+    ASSERT_EQ(output[i], secondOutput[i]) << "Outputs differ at index " << i;
+  }
+}
+
+
 
 /*
  * Test Cells4::_generateListsOfSynapsesToAdjustForAdaptSegment.