OmniFocus Format/Syncing Research

TL;DR


What is OmniFocus?

One of the best GTD/task management systems around: https://www.omnigroup.com/omnifocus

Part of building TodoMove means I need to integrate as many task management systems as I can; unfortunately OmniFocus doesn't provide an API so this has been troublesome, hence this blog post.

OmniSync

OmniFocus hosts its own servers to provide syncing between devices.

The main URL for their sync service is https://sync.omnigroup.com and each user also has their own URL with Basic Access Authentication at https://sync5.omnigroup.com/username

You can go to this URL in your browser to view your files, or you can use cadaver to browse it interactively: cadaver https://sync5.omnigroup.com/username/

It feels as if users are sharded across servers as my data is on sync5 as far as I can tell. sync.omnigroup.com helpfully redirects you to the right server for your data.

You can find the sync server by running curl https://sync.omnigroup.com/username/ or going to https://sync.omnigroup.com/username/ in your browser

WebDav

The main folder of interest in your WebDav sync is OmniFocus.ofocus, which houses these files of interest (FOI)

  • OmniFocus.ofocus/encrypted - XML file with decryption key (pasted below)
  • OmniFocus.ofocus/00000000000000=.{23}.zip - Encrypted (if you're on a recent version) file, with contents.xml inside
  • OmniFocus.ofocus/20161113150820=.{11}.client - Details per client (pasted below)

OmniFocus.ofocus/encrypted

Has a key to decrypt the encrypted files

<plist version="1.0">  
<array>  
<dict>  
<key>algorithm</key>  
<string>PBKDF2; aes128-wrap</string>  
<key>key</key>  
<data>  
ZK8X5pXH86rnZBxxxxxxxxnaDXLT0/5dLQ3vFpcvbSbOjhpJQcpMS6mSVwzO m61zkAxfNVsc6ualgXDfV9qkOgVz/Hek3l3RctFxnK251WxjdkfnRueiOx==  
</data>  
<key>method</key>  
<string>password</string>  
<key>rounds</key>  
<integer>1000000</integer>  
<key>salt</key>  
<data>EAFZAEAheQ3drToNPkl6YYN6ExX=</data>  
</dict>  
</array>  
</plist>  

OmniFocus.ofocus/00000000000000=.{23}.zip

This file stores contents.xml which is the full database of OmniFocus items including perspectives, perspective order, default dates, default times, default recurrences, folders, projects, contexts, tasks and more.

It also includes a data directory which, so far, I've only seen used to store custom images for perspectives.

contents.xml

The most useful file available (stores every bit of information OmniFocus uses); has multiple top level elements. Full example at the bottom of this post.

Top level items:

  • setting - Settings for the app
  • context - Children: added, modified, name, rank
  • folder - Children: added, modified, name, rank, hidden, folder (if it's a sub folder, then folder will have an idref of the parent folder)
  • task - Task or project. If it has a project element, then it's actually a project. Project Children: project (optional), added, modified, name, rank, context (with idref), order (parallel, sequential, single action), completed-by-children
    • If project: project has its own children:
      • folder (with idref)
      • singleton (bool)
      • last-review
      • next-review
      • review-interval
    • If task:
      • inbox (bool)
      • start
      • due
      • completed
      • estimated-minutes
      • repetition-method
      • flagged
  • perspective - View XML at the bottom of the page

OmniFocus.ofocus/20161113150820=.{11}.client

<plist version="1.0">  
<dict>  
<key>CurrentFrameworkVersion</key>  
<string>2</string>  
<key>HardwareCPUCount</key>  
<string>4</string>  
<key>HardwareCPUType</key>  
<string>7,8</string>  
<key>HardwareCPUTypeDescription</key>  
<string>Intel x86-64h Haswell</string>  
<key>HardwareCPUTypeName</key>  
<string>x86_64h</string>  
<key>HardwareModel</key>  
<string>MacBookPro11,1</string>  
<key>OFMSyncClientModelVersion</key>  
<string>4.4</string>  
<key>OFMSyncClientSupportedCapabilities</key>  
<array>  
<string>delta_transactions</string>  
</array>  
<key>OSVersion</key>  
<string>16A323</string>  
<key>OSVersionNumber</key>  
<string>10.12</string>  
<key>bundleIdentifier</key>  
<string>com.omnigroup.OmniFocus2</string>  
<key>bundleVersion</key>  
<string>109.13.0.270584</string>  
<key>clientIdentifier</key>  
<string>iOhyJVnxxX</string>  
<key>hostID</key>  
<string>XXXXXXXX-B1XE-XXXX-BDXF-8492XXXXXXXX</string>  
<key>lastSyncDate</key>  
<date>2016-11-13T15:08:20Z</date>  
<key>name</key>  
<string>Ashleys-MacBook-Pro.local</string>  
<key>registrationDate</key>  
<date>2015-05-20T18:34:41Z</date>  
<key>tailIdentifiers</key>  
<array>  
<string>hX7xfxX5uxx</string>  
</array>  
</dict>  
</plist>  

Helpful links and commands

Download your OmniFocus.ofocus folder
  • wget -r -nH -np --cut-dirs=1 --no-check-certificate -U Mozilla --user=[username] --password=[password] https://sync5.omnigroup.com/[username]/OmniFocus.ofocus
Decrypt your OmniFocus.ofocus/*.zip file

You may need to remove the 3.5 from these commands if you're not on a Mac. The decryption script requires at least Python 3.4.

  • brew install python3
  • pip3.5 install cryptography
  • python3.5 DecryptionExample.py -o newFolderForDecryptedContent OmniFocus.ofocus

contents.xml example

This is a full contents.xml file, pulled from an OmniFocus backup of a test setup.

<?xml version="1.0" encoding="UTF-8"?>  
<omnifocus xmlns="http://www.omnigroup.com/namespace/OmniFocus/v2" app-id="com.omnigroup.OmniFocus2" app-version="109.13.0.270584" os-name="Mac OS X" os-version="10.12" machine-model="MacBookPro11,1">  
   <setting id="OFMDefaultSingletonProjectPersistentIdentifier">
      <added order="1">2011-12-07T10:10:26.499Z</added>
      <modified>2015-05-20T18:35:06.551Z</modified>
      <plist version="1.0">
         <string>com.omnigroup.frameworks.OmniFocusModel.default_singleton_project.create_on_demand</string>
      </plist>
   </setting>
   <setting id="OFMRequiredRelationshipToProcessInboxItem">
      <added>2016-11-13T14:46:22.757Z</added>
      <plist version="1.0">
         <string>project</string>
      </plist>
   </setting>
   <setting id="OFMRemoteNotificationGroupID">
      <added>2016-11-13T14:46:31.274Z</added>
      <plist version="1.0">
         <string>xxxxxxxxxxxxxxxx442cac795a67ec4d3f9d973f8f942ce1xxxxxxxxxxxxxxxx</string>
      </plist>
   </setting>
   <setting id="OFMRemoteNotificationGroupIDLastRegenerationDate">
      <added order="1">2016-11-13T14:46:31.274Z</added>
      <plist version="1.0">
         <date>2016-11-13T14:46:28Z</date>
      </plist>
   </setting>
   <context id="abced123456">
      <added>2011-12-07T10:06:37.272Z</added>
      <modified>2016-03-21T21:58:36.582Z</modified>
      <name>Custom built context</name>
      <rank>2</rank>
   </context>
   <context id="bxp13MmTXLG">
      <added>2011-12-07T10:06:37.272Z</added>
      <modified>2016-03-21T21:58:36.582Z</modified>
      <name>People</name>
      <rank>1</rank>
   </context>
   <folder id="pOzQMpLcHxw">
      <added>2015-05-26T20:15:30.082Z</added>
      <modified>2016-08-12T16:51:51.456Z</modified>
      <name>Me</name>
      <rank>-1968526678</rank>
   </folder>
   <folder id="i1804qvPS3I">
      <folder idref="pOzQMpLcHxw" />
      <added>2015-05-26T20:20:43.166Z</added>
      <modified>2016-03-23T09:04:53.348Z</modified>
      <name>2016 Goals</name>
      <rank>0</rank>
      <hidden>false</hidden>
   </folder>
   <task id="axFWYCAeu8J">
      <project>
         <folder idref="i1804qvPS3I" />
         <singleton>true</singleton>
         <last-review>2015-05-25T23:00:00.000Z</last-review>
         <next-review>2015-06-01T23:00:00.000Z</next-review>
         <review-interval>@1w</review-interval>
      </project>
      <added>2015-05-26T20:20:54.468Z</added>
      <modified>2016-11-13T17:50:22.955Z</modified>
      <name>Rule the world</name>
      <rank>0</rank>
      <context idref="abced123456" />
      <order>parallel</order>
      <completed-by-children>true</completed-by-children>
   </task>
   <task id="lTpFc67l5UV">
      <project />
      <inbox>true</inbox>
      <task />
      <added>2016-11-13T17:27:42.849Z</added>
      <name>Write blog post on OmniFocus syncing/formats</name>
      <note />
      <rank>0</rank>
      <context />
      <start />
      <due />
      <completed />
      <estimated-minutes />
      <order>parallel</order>
      <flagged>false</flagged>
      <completed-by-children>false</completed-by-children>
      <repetition-rule />
      <repetition-method />
      <modified>2016-11-13T17:27:51.939Z</modified>
   </task>
   <task id="naFwlopkAFE">
      <project />
      <inbox>true</inbox>
      <task />
      <added>2016-11-13T17:27:46.420Z</added>
      <name>Win the lotto</name>
      <note />
      <rank>1073741824</rank>
      <context />
      <start />
      <due />
      <completed />
      <estimated-minutes />
      <order>parallel</order>
      <flagged>false</flagged>
      <completed-by-children>false</completed-by-children>
      <repetition-rule />
      <repetition-method />
      <modified>2016-11-13T17:27:55.189Z</modified>
   </task>
   <task id="k-tP-F1yqFe">
      <project />
      <inbox>false</inbox>
      <task idref="axFWYCAeu8J" />
      <added>2016-11-13T17:49:59.024Z</added>
      <name>Actual action</name>
      <note>
         <text>
            <p>
               <run>
                  <lit>My note is on fire</lit>
               </run>
            </p>
         </text>
      </note>
      <rank>0</rank>
      <context idref="bxp13MmTXLG" />
      <start>2016-11-14T00:00:00.000Z</start>
      <due>2016-11-20T17:00:00.000Z</due>
      <completed />
      <estimated-minutes />
      <order>parallel</order>
      <flagged>false</flagged>
      <completed-by-children>false</completed-by-children>
      <repetition-rule>FREQ=WEEKLY;BYDAY=WE,FR</repetition-rule>
      <repetition-method>fixed</repetition-method>
      <modified>2016-11-13T17:50:22.955Z</modified>
      <repeat>@1w</repeat>
   </task>
   <perspective id="ProcessFlagged">
      <added>2016-11-13T14:46:27.490Z</added>
      <plist version="1.0">
         <dict>
            <key>iconNameInBundle</key>
            <string>ProcessFlagged</string>
            <key>name</key>
            <string>Flagged</string>
            <key>uuid</key>
            <string>ProcessFlagged</string>
            <key>viewState</key>
            <dict>
               <key>viewMode</key>
               <string>context</string>
               <key>viewModeState</key>
               <dict>
                  <key>context</key>
                  <dict>
                     <key>actionCompletionFilter</key>
                     <string>incomplete</string>
                     <key>actionDurationFilter</key>
                     <string>any</string>
                     <key>actionFlaggedFilter</key>
                     <string>flagged</string>
                     <key>collation</key>
                     <string>context</string>
                     <key>firstResponder</key>
                     <string>actionOutline</string>
                     <key>sidebarFilter</key>
                     <string>available-contexts</string>
                     <key>sort</key>
                     <string>due</string>
                  </dict>
               </dict>
            </dict>
         </dict>
      </plist>
      <icon-attachment />
   </perspective>
   <perspective id="ProcessInbox">
      <added order="1">2016-11-13T14:46:27.490Z</added>
      <plist version="1.0">
         <dict>
            <key>iconNameInBundle</key>
            <string>ProcessInbox</string>
            <key>name</key>
            <string>Inbox</string>
            <key>useSavedSelection</key>
            <true />
            <key>uuid</key>
            <string>ProcessInbox</string>
            <key>viewState</key>
            <dict>
               <key>viewMode</key>
               <string>project</string>
               <key>viewModeState</key>
               <dict>
                  <key>project</key>
                  <dict>
                     <key>actionCompletionFilter</key>
                     <string>incomplete</string>
                     <key>actionDurationFilter</key>
                     <string>any</string>
                     <key>actionFlaggedFilter</key>
                     <string>all</string>
                     <key>collation</key>
                     <string>none</string>
                     <key>firstResponder</key>
                     <string>actionOutline</string>
                     <key>sidebar</key>
                     <dict>
                        <key>selected</key>
                        <array>
                           <array>
                              <string>com.omnigroup.omnifocus.live_fetch.inbox</string>
                           </array>
                        </array>
                     </dict>
                     <key>sidebarFilter</key>
                     <string>remaining-projects</string>
                     <key>sort</key>
                     <string>none</string>
                  </dict>
               </dict>
            </dict>
         </dict>
      </plist>
      <icon-attachment />
   </perspective>
   <perspective id="ProcessRecentChanges">
      <added order="2">2016-11-13T14:46:27.490Z</added>
      <plist version="1.0">
         <dict>
            <key>iconNameInBundle</key>
            <string>perspectivelist_new_calendar</string>
            <key>name</key>
            <string>Changed</string>
            <key>uuid</key>
            <string>ProcessRecentChanges</string>
            <key>viewState</key>
            <dict>
               <key>viewMode</key>
               <string>context</string>
               <key>viewModeState</key>
               <dict>
                  <key>context</key>
                  <dict>
                     <key>actionCompletionFilter</key>
                     <string>all</string>
                     <key>actionDurationFilter</key>
                     <string>any</string>
                     <key>actionFlaggedFilter</key>
                     <string>all</string>
                     <key>collation</key>
                     <string>modified</string>
                     <key>firstResponder</key>
                     <string>actionOutline</string>
                     <key>sidebarFilter</key>
                     <string>all-contexts</string>
                     <key>sort</key>
                     <string>modified</string>
                  </dict>
               </dict>
            </dict>
         </dict>
      </plist>
      <icon-attachment />
   </perspective>
   <perspective id="ProcessProjects">
      <added order="3">2016-11-13T14:46:27.490Z</added>
      <plist version="1.0">
         <dict>
            <key>iconNameInBundle</key>
            <string>ProcessProjects</string>
            <key>name</key>
            <string>Projects</string>
            <key>uuid</key>
            <string>ProcessProjects</string>
            <key>viewState</key>
            <dict>
               <key>viewMode</key>
               <string>project</string>
               <key>viewModeState</key>
               <dict>
                  <key>project</key>
                  <dict>
                     <key>actionCompletionFilter</key>
                     <string>incomplete</string>
                     <key>actionDurationFilter</key>
                     <string>any</string>
                     <key>actionFlaggedFilter</key>
                     <string>all</string>
                     <key>collation</key>
                     <string>none</string>
                     <key>firstResponder</key>
                     <string>actionOutline</string>
                     <key>sidebar</key>
                     <dict>
                        <key>selected</key>
                        <array>
                           <array>
                              <string>com.omnigroup.omnifocus.project_sidebar.library</string>
                           </array>
                        </array>
                     </dict>
                     <key>sidebarFilter</key>
                     <string>remaining-projects</string>
                     <key>sort</key>
                     <string>none</string>
                  </dict>
               </dict>
            </dict>
         </dict>
      </plist>
      <icon-attachment />
   </perspective>
   <perspective id="ProcessDueSoon">
      <added order="4">2016-11-13T14:46:27.490Z</added>
      <plist version="1.0">
         <dict>
            <key>iconNameInBundle</key>
            <string>ProcessDueSoon</string>
            <key>name</key>
            <string>Due</string>
            <key>uuid</key>
            <string>ProcessDueSoon</string>
            <key>viewState</key>
            <dict>
               <key>viewMode</key>
               <string>context</string>
               <key>viewModeState</key>
               <dict>
                  <key>context</key>
                  <dict>
                     <key>actionCompletionFilter</key>
                     <string>due</string>
                     <key>actionDurationFilter</key>
                     <string>any</string>
                     <key>actionFlaggedFilter</key>
                     <string>all</string>
                     <key>collation</key>
                     <string>due</string>
                     <key>firstResponder</key>
                     <string>actionOutline</string>
                     <key>sidebarFilter</key>
                     <string>available-contexts</string>
                     <key>sort</key>
                     <string>due</string>
                  </dict>
               </dict>
            </dict>
         </dict>
      </plist>
      <icon-attachment />
   </perspective>
   <perspective id="ProcessCompleted">
      <added order="5">2016-11-13T14:46:27.490Z</added>
      <plist version="1.0">
         <dict>
            <key>iconNameInBundle</key>
            <string>ProcessCompleted</string>
            <key>name</key>
            <string>Completed</string>
            <key>uuid</key>
            <string>ProcessCompleted</string>
            <key>viewState</key>
            <dict>
               <key>viewMode</key>
               <string>context</string>
               <key>viewModeState</key>
               <dict>
                  <key>context</key>
                  <dict>
                     <key>actionCompletionFilter</key>
                     <string>complete</string>
                     <key>actionDurationFilter</key>
                     <string>any</string>
                     <key>actionFlaggedFilter</key>
                     <string>all</string>
                     <key>collation</key>
                     <string>completed</string>
                     <key>firstResponder</key>
                     <string>actionOutline</string>
                     <key>sidebarFilter</key>
                     <string>all-contexts</string>
                     <key>sort</key>
                     <string>completed</string>
                  </dict>
               </dict>
            </dict>
         </dict>
      </plist>
      <icon-attachment />
   </perspective>
   <perspective id="ProcessReview">
      <added order="6">2016-11-13T14:46:27.490Z</added>
      <plist version="1.0">
         <dict>
            <key>iconNameInBundle</key>
            <string>ProcessReview</string>
            <key>name</key>
            <string>Review</string>
            <key>uuid</key>
            <string>ProcessReview</string>
            <key>viewState</key>
            <dict>
               <key>viewMode</key>
               <string>project</string>
               <key>viewModeState</key>
               <dict>
                  <key>project</key>
                  <dict>
                     <key>actionCompletionFilter</key>
                     <string>incomplete</string>
                     <key>actionDurationFilter</key>
                     <string>any</string>
                     <key>actionFlaggedFilter</key>
                     <string>all</string>
                     <key>collation</key>
                     <string>next-review</string>
                     <key>firstResponder</key>
                     <string>actionOutline</string>
                     <key>sidebarFilter</key>
                     <string>remaining-projects</string>
                     <key>sort</key>
                     <string>none</string>
                  </dict>
               </dict>
            </dict>
         </dict>
      </plist>
      <icon-attachment />
   </perspective>
   <perspective id="ProcessContexts">
      <added order="7">2016-11-13T14:46:27.490Z</added>
      <plist version="1.0">
         <dict>
            <key>iconNameInBundle</key>
            <string>ProcessContexts</string>
            <key>name</key>
            <string>Contexts</string>
            <key>uuid</key>
            <string>ProcessContexts</string>
            <key>viewState</key>
            <dict>
               <key>viewMode</key>
               <string>context</string>
               <key>viewModeState</key>
               <dict>
                  <key>context</key>
                  <dict>
                     <key>actionCompletionFilter</key>
                     <string>incomplete</string>
                     <key>actionDurationFilter</key>
                     <string>any</string>
                     <key>actionFlaggedFilter</key>
                     <string>all</string>
                     <key>collation</key>
                     <string>context</string>
                     <key>firstResponder</key>
                     <string>actionOutline</string>
                     <key>sidebarFilter</key>
                     <string>available-contexts</string>
                     <key>sort</key>
                     <string>project</string>
                  </dict>
               </dict>
            </dict>
         </dict>
      </plist>
      <icon-attachment />
   </perspective>
</omnifocus>