diff --git a/resources/sql/20230427.semistructured.0.edge.sql b/resources/sql/20230427.semistructured.0.edge.sql
new file mode 100644
index 0000000..f6c647b
--- /dev/null
+++ b/resources/sql/20230427.semistructured.0.edge.sql
@@ -0,0 +1,3 @@
+CREATE TABLE {$NAMESPACE}_semistructured.edge LIKE {$NAMESPACE}_file.edge;
+CREATE TABLE {$NAMESPACE}_semistructured.edgedata
+  LIKE {$NAMESPACE}_file.edgedata;
diff --git a/resources/sql/20230427.semistructured.1.objecttype.sql b/resources/sql/20230427.semistructured.1.objecttype.sql
index 6e9fbc0..d1f757c 100644
--- a/resources/sql/20230427.semistructured.1.objecttype.sql
+++ b/resources/sql/20230427.semistructured.1.objecttype.sql
@@ -1,24 +1,26 @@
 CREATE TABLE {$NAMESPACE}_semistructured.semistructured_objecttype (
   id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
   phid VARBINARY(64) NOT NULL,
+  viewPolicy varbinary(64) NOT NULL,
+  editPolicy varbinary(64) NOT NULL,
   dateCreated INT UNSIGNED NOT NULL,
   dateModified INT UNSIGNED NOT NULL,
 
   name VARCHAR(255) NOT NULL COLLATE {$COLLATE_SORT},
   description LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
   status VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT},
 
   UNIQUE KEY `key_phid` (phid)
 ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
 
--- CREATE TABLE {$NAMESPACE}_semistructured.semistructured_objecttypetransaction
---   LIKE {$NAMESPACE}_file.file_transaction;
+CREATE TABLE {$NAMESPACE}_semistructured.semistructured_objecttypetransaction
+  LIKE {$NAMESPACE}_file.file_transaction;
 
 CREATE TABLE {$NAMESPACE}_semistructured.semistructured_objecttype_ffield
   LIKE {$NAMESPACE}_dashboard.dashboard_dashboard_ffield;
 CREATE TABLE {$NAMESPACE}_semistructured.semistructured_objecttype_fdocument
   LIKE {$NAMESPACE}_dashboard.dashboard_dashboard_fdocument;
 CREATE TABLE {$NAMESPACE}_semistructured.semistructured_objecttype_fngrams
   LIKE {$NAMESPACE}_dashboard.dashboard_dashboard_fngrams;
 CREATE TABLE {$NAMESPACE}_semistructured.semistructured_objecttype_fngrams_common
   LIKE {$NAMESPACE}_dashboard.dashboard_dashboard_fngrams_common;
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
index 872b200..6b71c24 100644
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1,55 +1,69 @@
 <?php
 
 /**
  * This file is automatically generated. Use 'arc liberate' to rebuild it.
  *
  * @generated
  * @phutil-library-version 2
  */
 phutil_register_library_map(array(
   '__library_version__' => 2,
   'class' => array(
     'SemiStructuredBaseController' => 'applications/semistruct/controller/SemiStructuredBaseController.php',
     'SemiStructuredDAO' => 'applications/semistruct/storage/SemiStructuredDAO.php',
     'SemiStructuredDataApplication' => 'applications/semistruct/application/SemiStructuredDataApplication.php',
     'SemiStructuredObjectInstance' => 'applications/semistruct/storage/SemiStructuredObjectInstance.php',
     'SemiStructuredObjectInstancePHIDType' => 'applications/semistruct/phid/SemiStructuredObjectInstancePHIDType.php',
     'SemiStructuredObjectInstanceQuery' => 'applications/semistruct/query/SemiStructuredObjectInstanceQuery.php',
     'SemiStructuredObjectType' => 'applications/semistruct/storage/SemiStructuredObjectType.php',
+    'SemiStructuredObjectTypeDescriptionTransaction' => 'applications/semistruct/xaction/class/SemiStructuredObjectTypeDescriptionTransaction.php',
+    'SemiStructuredObjectTypeEditController' => 'applications/semistruct/controller/class/SemiStructuredObjectTypeEditController.php',
+    'SemiStructuredObjectTypeEditEngine' => 'applications/semistruct/editor/SemiStructuredObjectTypeEditEngine.php',
     'SemiStructuredObjectTypeFerretEngine' => 'applications/semistruct/engine/SemiStructuredObjectTypeFerretEngine.php',
-    'SemiStructuredObjectTypeListController' => 'applications/semistruct/controller/SemiStructuredObjectTypeListController.php',
+    'SemiStructuredObjectTypeListController' => 'applications/semistruct/controller/class/SemiStructuredObjectTypeListController.php',
+    'SemiStructuredObjectTypeNameTransaction' => 'applications/semistruct/xaction/class/SemiStructuredObjectTypeNameTransaction.php',
     'SemiStructuredObjectTypePHIDType' => 'applications/semistruct/phid/SemiStructuredObjectTypePHIDType.php',
     'SemiStructuredObjectTypeQuery' => 'applications/semistruct/query/SemiStructuredObjectTypeQuery.php',
     'SemiStructuredObjectTypeSearchEngine' => 'applications/semistruct/query/SemiStructuredObjectTypeSearchEngine.php',
+    'SemiStructuredObjectTypeTransaction' => 'applications/semistruct/storage/SemiStructuredObjectTypeTransaction.php',
+    'SemiStructuredObjectTypeTransactionEditor' => 'applications/semistruct/editor/SemiStructuredObjectTypeTransactionEditor.php',
+    'SemiStructuredObjectTypeTransactionType' => 'applications/semistruct/xaction/class/SemiStructuredObjectTypeTransactionType.php',
     'SemiStructuredPatchList' => 'applications/semistruct/storage/SemiStructuredPatchList.php',
   ),
   'function' => array(),
   'xmap' => array(
     'SemiStructuredBaseController' => 'PhabricatorController',
     'SemiStructuredDAO' => 'PhabricatorLiskDAO',
     'SemiStructuredDataApplication' => 'PhabricatorApplication',
     'SemiStructuredObjectInstance' => array(
       'SemiStructuredDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorDestructibleInterface',
     ),
     'SemiStructuredObjectInstancePHIDType' => 'PhabricatorPHIDType',
     'SemiStructuredObjectInstanceQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'SemiStructuredObjectType' => array(
       'SemiStructuredDAO',
       'PhabricatorPolicyInterface',
       'PhabricatorApplicationTransactionInterface',
       'PhabricatorFlaggableInterface',
       'PhabricatorDestructibleInterface',
       'PhabricatorFerretInterface',
     ),
+    'SemiStructuredObjectTypeDescriptionTransaction' => 'SemiStructuredObjectTypeTransactionType',
+    'SemiStructuredObjectTypeEditController' => 'PhabricatorDashboardController',
+    'SemiStructuredObjectTypeEditEngine' => 'PhabricatorEditEngine',
     'SemiStructuredObjectTypeFerretEngine' => 'PhabricatorFerretEngine',
     'SemiStructuredObjectTypeListController' => 'SemiStructuredBaseController',
+    'SemiStructuredObjectTypeNameTransaction' => 'SemiStructuredObjectTypeTransactionType',
     'SemiStructuredObjectTypePHIDType' => 'PhabricatorPHIDType',
     'SemiStructuredObjectTypeQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
     'SemiStructuredObjectTypeSearchEngine' => 'PhabricatorApplicationSearchEngine',
+    'SemiStructuredObjectTypeTransaction' => 'PhabricatorModularTransaction',
+    'SemiStructuredObjectTypeTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
+    'SemiStructuredObjectTypeTransactionType' => 'PhabricatorModularTransactionType',
     'SemiStructuredPatchList' => 'PhabricatorSQLPatchList',
   ),
 ));
diff --git a/src/application/SemiStructuredDataApplication.php b/src/application/SemiStructuredDataApplication.php
index 4c0022f..4639307 100644
--- a/src/application/SemiStructuredDataApplication.php
+++ b/src/application/SemiStructuredDataApplication.php
@@ -1,25 +1,27 @@
 <?php
 
 final class SemiStructuredDataApplication extends PhabricatorApplication {
   public function getName() {
     return pht('Semi-Structured');
   }
 
   public function getShortDescription() {
     return pht('Work with evolving data sets.');
   }
 
   public function getBaseURI() {
     return '/semistruct/';
   }
 
   public function getRoutes() {
     return array(
         '/semistruct/' => array(
           '(?:query/(?P<queryKey>[^/]+)/)?' =>
             'SemiStructuredObjectTypeListController',
+          $this->getEditRoutePattern('editclass/') =>
+            'SemiStructuredObjectTypeEditController',
       ),
     );
   }
 
 }
diff --git a/src/controller/class/SemiStructuredObjectTypeEditController.php b/src/controller/class/SemiStructuredObjectTypeEditController.php
new file mode 100644
index 0000000..7cb0880
--- /dev/null
+++ b/src/controller/class/SemiStructuredObjectTypeEditController.php
@@ -0,0 +1,12 @@
+<?php
+
+final class SemiStructuredObjectTypeEditController
+  extends PhabricatorDashboardController {
+
+  public function handleRequest(AphrontRequest $request) {
+    return id(new SemiStructuredObjectTypeEditEngine())
+      ->setController($this)
+      ->buildResponse();
+  }
+
+}
diff --git a/src/controller/SemiStructuredObjectTypeListController.php b/src/controller/class/SemiStructuredObjectTypeListController.php
similarity index 88%
rename from src/controller/SemiStructuredObjectTypeListController.php
rename to src/controller/class/SemiStructuredObjectTypeListController.php
index f9d30fa..9640b2a 100644
--- a/src/controller/SemiStructuredObjectTypeListController.php
+++ b/src/controller/class/SemiStructuredObjectTypeListController.php
@@ -1,41 +1,41 @@
 <?php
 
 final class SemiStructuredObjectTypeListController
   extends SemiStructuredBaseController {
 
   public function handleRequest(AphrontRequest $request) {
     $viewer = $request->getViewer();
     $query_key = $request->getURIData('queryKey');
 
     $controller = id(new PhabricatorApplicationSearchController())
       ->setQueryKey($query_key)
       ->setSearchEngine(new SemiStructuredObjectTypeSearchEngine())
       ->setNavigation($this->buildSideNavView());
     return $this->delegateToController($controller);
   }
 
   public function buildSideNavView() {
     $user = $this->getRequest()->getUser();
 
     $nav = new AphrontSideNavFilterView();
     $nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
 
     id(new SemiStructuredObjectTypeSearchEngine())
       ->setViewer($user)
       ->addNavigationItems($nav->getMenu());
 
     $nav->selectFilter(null);
 
     return $nav;
   }
 
   protected function buildApplicationCrumbs() {
     $crumbs = parent::buildApplicationCrumbs();
 
-//    id(new SemiStructuredObjectTypeEditEngine()) // TODO
-  //    ->setViewer($this->getViewer())
-    //  ->addActionToCrumbs($crumbs);
+   id(new SemiStructuredObjectTypeEditEngine())
+     ->setViewer($this->getViewer())
+     ->addActionToCrumbs($crumbs);
 
     return $crumbs;
   }
 }
diff --git a/src/editor/SemiStructuredObjectTypeEditEngine.php b/src/editor/SemiStructuredObjectTypeEditEngine.php
new file mode 100644
index 0000000..5a1ff18
--- /dev/null
+++ b/src/editor/SemiStructuredObjectTypeEditEngine.php
@@ -0,0 +1,99 @@
+<?php
+
+final class SemiStructuredObjectTypeEditEngine
+  extends PhabricatorEditEngine {
+
+  const ENGINECONST = 'sst:objecttype';
+
+  public function isEngineConfigurable() {
+    return false;
+  }
+
+  public function getEngineName() {
+    return pht('Object Type');
+  }
+
+  public function getSummaryHeader() {
+    return pht('Edit Object Type');
+  }
+
+  public function getSummaryText() {
+    return pht('This engine is used to modify object types.');
+  }
+
+  public function getEngineApplicationClass() {
+    return 'SemiStructuredDataApplication';
+  }
+
+  protected function newEditableObject() {
+    $viewer = $this->getViewer();
+    return SemiStructuredObjectType::initializeNewObjectType($viewer);
+  }
+
+  protected function newObjectQuery() {
+    return new SemiStructuredObjectTypeQuery();
+  }
+
+  protected function getObjectCreateTitleText($object) {
+    return pht('Create Object Type');
+  }
+
+  protected function getObjectCreateButtonText($object) {
+    return pht('Create Object Type');
+  }
+
+  protected function getObjectCreateCancelURI($object) {
+    return '/semistructured/';
+  }
+
+  protected function getEditorURI() {
+    return $this->getApplication()->getApplicationURI('editclass/');
+  }
+
+  protected function getObjectEditTitleText($object) {
+    return pht('Edit Object Type: %s', $object->getName());
+  }
+
+  protected function getObjectEditShortText($object) {
+    return pht('Edit Object Type');
+  }
+
+  protected function getObjectCreateShortText() {
+    return pht('Create Object Type');
+  }
+
+  protected function getObjectName() {
+    return pht('Object Type');
+  }
+
+  protected function getObjectViewURI($object) {
+    return $object->getURI();
+  }
+
+  protected function buildCustomEditFields($object) {
+
+    $fields = array(
+      id(new PhabricatorTextEditField())
+        ->setKey('name')
+        ->setLabel(pht('Name'))
+        ->setDescription(pht('Name of the object type.'))
+        ->setConduitDescription(pht('Rename the type.'))
+        ->setConduitTypeDescription(pht('New type name.'))
+        ->setTransactionType(
+          SemiStructuredObjectTypeNameTransaction::TRANSACTIONTYPE)
+        ->setIsRequired(true)
+        ->setValue($object->getName()),
+      id(new PhabricatorRemarkupEditField())
+        ->setKey('description')
+        ->setLabel(pht('Description'))
+        ->setDescription(pht('Object Type long description.'))
+        ->setConduitTypeDescription(pht('New type description.'))
+        ->setTransactionType(
+          SemiStructuredObjectTypeDescriptionTransaction::TRANSACTIONTYPE)
+        ->setValue($object->getDescription()),
+    );
+
+    return $fields;
+  }
+
+}
diff --git a/src/editor/SemiStructuredObjectTypeTransactionEditor.php b/src/editor/SemiStructuredObjectTypeTransactionEditor.php
new file mode 100644
index 0000000..6d9dc24
--- /dev/null
+++ b/src/editor/SemiStructuredObjectTypeTransactionEditor.php
@@ -0,0 +1,28 @@
+<?php
+
+final class SemiStructuredObjectTypeTransactionEditor
+  extends PhabricatorApplicationTransactionEditor {
+
+  public function getEditorApplicationClass() {
+    return 'PhabricatorDashboardApplication';
+  }
+
+  public function getEditorObjectsDescription() {
+    return pht('Dashboard Panels');
+  }
+
+  public function getTransactionTypes() {
+    $types = parent::getTransactionTypes();
+
+    $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
+    $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
+    $types[] = PhabricatorTransactions::TYPE_EDGE;
+
+    return $types;
+  }
+
+  protected function supportsSearch() {
+    return true;
+  }
+
+}
diff --git a/src/query/SemiStructuredObjectTypeSearchEngine.php b/src/query/SemiStructuredObjectTypeSearchEngine.php
index 82b09a9..af9f8bf 100644
--- a/src/query/SemiStructuredObjectTypeSearchEngine.php
+++ b/src/query/SemiStructuredObjectTypeSearchEngine.php
@@ -1,153 +1,153 @@
 <?php
 
 final class SemiStructuredObjectTypeSearchEngine
   extends PhabricatorApplicationSearchEngine {
 
   public function getResultTypeDescription() {
     return pht('Object Types');
   }
 
   public function getApplicationClassName() {
     return 'SemiStructuredDataApplication';
   }
 
   public function newQuery() {
     return id(new SemiStructuredObjectTypeQuery());
   }
 
   protected function buildCustomSearchFields() {
     return array(
       id(new PhabricatorSearchCheckboxesField())
         ->setKey('statuses')
         ->setLabel(pht('Status'))
-        ->setOptions(PhabricatorDashboard::getStatusNameMap()),
+        ->setOptions(SemiStructuredObjectType::getStatusNameMap()),
       id(new PhabricatorSearchCheckboxesField())
         ->setKey('editable')
         ->setLabel(pht('Editable'))
         ->setOptions(array('editable' => null)),
     );
   }
 
   protected function getURI($path) {
     return '/semistruct/'.$path;
   }
 
   protected function getBuiltinQueryNames() {
     $names = array();
 
     $names['all'] = pht('All Types');
     $names['open'] = pht('Active Types');
 
     return $names;
   }
 
   public function buildSavedQueryFromBuiltin($query_key) {
     $query = $this->newSavedQuery();
     $query->setQueryKey($query_key);
     $viewer = $this->requireViewer();
 
     switch ($query_key) {
       case 'all':
         return $query;
       case 'open':
         return $query->setParameter(
           'statuses',
           array(
             SemiStructuredObjectType::STATUS_ACTIVE,
           ));
     }
 
     return parent::buildSavedQueryFromBuiltin($query_key);
   }
 
 
   protected function buildQueryFromParameters(array $map) {
     $query = $this->newQuery();
 
     if ($map['statuses']) {
       $query->withStatuses($map['statuses']);
     }
 
     if ($map['editable'] !== null) {
       $query->withCanEdit($map['editable']);
     }
 
     return $query;
   }
 
   protected function renderResultList(
     array $items,
     PhabricatorSavedQuery $query,
     array $handles) {
 
     $viewer = $this->requireViewer();
 
     $phids = array();
     foreach ($items as $item) {
       // $author_phid = $dashboard->getAuthorPHID();
       // if ($author_phid) {
       //   $phids[] = $author_phid;
       // }
     }
 
     $handles = $viewer->loadHandles($phids);
 
     if ($items) {
       $edge_query = id(new PhabricatorEdgeQuery())
         ->withSourcePHIDs(mpull($items, 'getPHID'))
         ->withEdgeTypes(
           array(
             PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
           ));
 
       $edge_query->execute();
     }
 
     $list = id(new PHUIObjectItemListView())
       ->setViewer($viewer);
 
-    foreach ($items as $item) {
+    foreach ($items as $class) {
       $item = id(new PHUIObjectItemView())
         ->setViewer($viewer)
         ->setObjectName($item->getObjectName())
         ->setHeader($item->getName())
         ->setHref($item->getURI())
         ->setObject($item);
 
-      if ($item->isArchived()) {
+      if ($class->isArchived()) {
         $item->setDisabled(true);
         $bg_color = 'bg-grey';
       } else {
         $bg_color = 'bg-dark';
       }
 
       $icon = id(new PHUIIconView())
-        ->setIcon($item->getIcon())
+        ->setIcon($class->getIcon())
         ->setBackground($bg_color);
       $item->setImageIcon($icon);
-      $item->setEpoch($item->getDateModified());
+      $item->setEpoch($class->getDateModified());
 
 
-      $phid = $item->getPHID();
+      $phid = $class->getPHID();
       $project_phids = $edge_query->getDestinationPHIDs(array($phid));
       $project_handles = $viewer->loadHandles($project_phids);
 
       $item->addAttribute(
         id(new PHUIHandleTagListView())
           ->setLimit(4)
           ->setNoDataString(pht('No Tags'))
           ->setSlim(true)
           ->setHandles($project_handles));
 
       $list->addItem($item);
     }
 
     $result = new PhabricatorApplicationSearchResultView();
     $result->setObjectList($list);
     $result->setNoDataString(pht('No object types found.'));
 
     return $result;
   }
 
 
 }
diff --git a/src/storage/SemiStructuredObjectInstance.php b/src/storage/SemiStructuredObjectInstance.php
index c8dc301..ec37143 100644
--- a/src/storage/SemiStructuredObjectInstance.php
+++ b/src/storage/SemiStructuredObjectInstance.php
@@ -1,107 +1,111 @@
 <?php
 
 final class SemiStructuredObjectInstance extends SemiStructuredDAO
   implements
     PhabricatorPolicyInterface,
     PhabricatorApplicationTransactionInterface,
     // PhabricatorSubscribableInterface,
     PhabricatorFlaggableInterface,
     PhabricatorDestructibleInterface
     // PhabricatorConduitResultInterface
     {
 
 
   protected $classPHID;
   protected $status;
   protected $rawData;
 
 
 
   const STATUS_ACTIVE = 'active';
   const STATUS_ARCHIVED = 'archived';
 
 
   public static function initializeNewObjectInstance(
       PhabricatorUser $actor,
       SemiStructuredObjectType $object_class) {
 
     return id(new self())
       ->setClassPHID($object_class->getPHID())
       ->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy()) // TODO take policies from class?
       ->setEditPolicy($actor->getPHID())
       ->setStatus(self::STATUS_ACTIVE);
   }
 
   public static function getStatusNameMap() {
     return array(
       self::STATUS_ACTIVE => pht('Active'),
       self::STATUS_ARCHIVED => pht('Archived'),
     );
   }
 
   protected function getConfiguration() {
     return array(
       self::CONFIG_AUX_PHID => true,
       self::CONFIG_COLUMN_SCHEMA => array(
         'name' => 'sort255',
         'status' => 'text32',
         'classPHID' => 'phid',
         'rawData' => 'text',
       ),
       self::CONFIG_KEY_SCHEMA => array(
         'classPHID' => array(
           'columns' => array('classPHID'),
         ),
       ),
     ) + parent::getConfiguration();
   }
 
 
+  public function getPHIDType() {
+    return SemiStructuredObjectInstancePHIDType::TYPECONST;
+  }
+
 /* -(  PhabricatorApplicationTransactionInterface  )------------------------- */
 
 
   public function getApplicationTransactionEditor() {
-    return new PhabricatorDashboardPanelTransactionEditor();
+    // return new PhabricatorDashboardPanelTransactionEditor(); TODO
   }
 
   public function getApplicationTransactionTemplate() {
-    return new PhabricatorDashboardPanelTransaction();
+    // return new PhabricatorDashboardPanelTransaction(); TODO
   }
 
 
 /* -(  PhabricatorPolicyInterface  )----------------------------------------- */
 
 
   public function getCapabilities() {
     return array(
       PhabricatorPolicyCapability::CAN_VIEW,
       PhabricatorPolicyCapability::CAN_EDIT,
     );
   }
 
   public function getPolicy($capability) {
     switch ($capability) {
       case PhabricatorPolicyCapability::CAN_VIEW:
         return $this->getViewPolicy();
       case PhabricatorPolicyCapability::CAN_EDIT:
         return $this->getEditPolicy();
     }
   }
 
   public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
     return false;
   }
 
 
 /* -(  PhabricatorDestructibleInterface  )----------------------------------- */
 
 
   public function destroyObjectPermanently(
     PhabricatorDestructionEngine $engine) {
 
     $this->openTransaction();
       $this->delete();
     $this->saveTransaction();
   }
 
 }
diff --git a/src/storage/SemiStructuredObjectType.php b/src/storage/SemiStructuredObjectType.php
index fcf849d..642f611 100644
--- a/src/storage/SemiStructuredObjectType.php
+++ b/src/storage/SemiStructuredObjectType.php
@@ -1,106 +1,132 @@
 <?php
 
 final class SemiStructuredObjectType extends SemiStructuredDAO
   implements
     PhabricatorPolicyInterface,
     PhabricatorApplicationTransactionInterface,
     // PhabricatorSubscribableInterface,
     PhabricatorFlaggableInterface,
     PhabricatorDestructibleInterface,
     PhabricatorFerretInterface
     // PhabricatorConduitResultInterface
      {
 
 
   protected $name;
   protected $status;
   protected $description;
+  protected $viewPolicy;
+  protected $editPolicy;
+
+// custom fields
+// icon/logo
 
 
 
   const STATUS_ACTIVE = 'active';
   const STATUS_ARCHIVED = 'archived';
 
 
   public static function initializeNewObjectType(PhabricatorUser $actor) {
     return id(new self())
       ->setName('')
       ->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy())
-      ->setEditPolicy($actor->getPHID())  // TODO config for deflt
+      ->setEditPolicy($actor->getPHID())
       ->setStatus(self::STATUS_ACTIVE);
   }
 
   public static function getStatusNameMap() {
     return array(
       self::STATUS_ACTIVE => pht('Active'),
       self::STATUS_ARCHIVED => pht('Archived'),
     );
   }
 
   protected function getConfiguration() {
     return array(
       self::CONFIG_AUX_PHID => true,
       self::CONFIG_COLUMN_SCHEMA => array(
         'name' => 'sort255',
         'status' => 'text32',
         'description' => 'text',
       ),
     ) + parent::getConfiguration();
   }
 
+  public function getPHIDType() {
+    return SemiStructuredObjectTypePHIDType::TYPECONST;
+  }
+
+  public function isArchived() {
+    return ($this->getStatus() == self::STATUS_ARCHIVED);
+  }
+
+  public function getURI() {
+    return urisprintf('/semistruct/type/%d/', $this->getID());
+  }
+
+  public function getObjectName() {
+    return pht('Object Type %d', $this->getID());
+  }
+
+  public function getIcon() {
+    // TODO
+    return 'fa-bear';
+  }
 
 
 /* -(  PhabricatorApplicationTransactionInterface  )------------------------- */
 
 
   public function getApplicationTransactionEditor() {
-    return new PhabricatorDashboardPanelTransactionEditor();
+    return new SemiStructuredObjectTypeTransactionEditor();
   }
 
   public function getApplicationTransactionTemplate() {
-    return new PhabricatorDashboardPanelTransaction();
+    return new SemiStructuredObjectTypeTransaction();
   }
 
 
 /* -(  PhabricatorPolicyInterface  )----------------------------------------- */
 
 
   public function getCapabilities() {
     return array(
       PhabricatorPolicyCapability::CAN_VIEW,
       PhabricatorPolicyCapability::CAN_EDIT,
+      // TODO can-create-instances
     );
   }
 
   public function getPolicy($capability) {
     switch ($capability) {
       case PhabricatorPolicyCapability::CAN_VIEW:
         return $this->getViewPolicy();
       case PhabricatorPolicyCapability::CAN_EDIT:
         return $this->getEditPolicy();
     }
   }
 
   public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
     return false;
   }
 
 
 /* -(  PhabricatorDestructibleInterface  )----------------------------------- */
 
 
   public function destroyObjectPermanently(
     PhabricatorDestructionEngine $engine) {
 
     $this->openTransaction();
       $this->delete();
     $this->saveTransaction();
   }
 
 /* -(  PhabricatorFerretInterface  )----------------------------------------- */
 
   public function newFerretEngine() {
     return new SemiStructuredObjectTypeFerretEngine();
   }
 
 }
diff --git a/src/storage/SemiStructuredObjectTypeTransaction.php b/src/storage/SemiStructuredObjectTypeTransaction.php
new file mode 100644
index 0000000..eea9346
--- /dev/null
+++ b/src/storage/SemiStructuredObjectTypeTransaction.php
@@ -0,0 +1,18 @@
+<?php
+
+final class SemiStructuredObjectTypeTransaction
+  extends PhabricatorModularTransaction {
+
+  public function getApplicationName() {
+    return 'semistructured';
+  }
+
+  public function getApplicationTransactionType() {
+    return SemiStructuredObjectTypePHIDType::TYPECONST;
+  }
+
+  public function getBaseTransactionClass() {
+    return 'SemiStructuredObjectTypeTransactionType';
+  }
+
+}
diff --git a/src/xaction/class/SemiStructuredObjectTypeDescriptionTransaction.php b/src/xaction/class/SemiStructuredObjectTypeDescriptionTransaction.php
new file mode 100644
index 0000000..e6ae6fa
--- /dev/null
+++ b/src/xaction/class/SemiStructuredObjectTypeDescriptionTransaction.php
@@ -0,0 +1,57 @@
+<?php
+
+final class SemiStructuredObjectTypeDescriptionTransaction
+  extends SemiStructuredObjectTypeTransactionType {
+
+  const TRANSACTIONTYPE = 'semist:class:description';
+
+  public function generateOldValue($object) {
+    return $object->getDescription();
+  }
+
+  public function applyInternalEffects($object, $value) {
+    $object->setDescription($value);
+  }
+
+  public function getTitle() {
+    return pht(
+      '%s updated the object type description.',
+      $this->renderAuthor());
+  }
+
+  public function getTitleForFeed() {
+    return pht(
+      '%s updated the description for object type %s.',
+      $this->renderAuthor(),
+      $this->renderObject());
+  }
+
+  public function hasChangeDetailView() {
+    return true;
+  }
+
+  public function getMailDiffSectionHeader() {
+    return pht('CHANGES TO OBJECT TYPE DESCRIPTION');
+  }
+
+  public function newChangeDetailView() {
+    $viewer = $this->getViewer();
+
+    return id(new PhabricatorApplicationTransactionTextDiffDetailView())
+      ->setViewer($viewer)
+      ->setOldText($this->getOldValue())
+      ->setNewText($this->getNewValue());
+  }
+
+  public function newRemarkupChanges() {
+    $changes = array();
+
+    $changes[] = $this->newRemarkupChange()
+      ->setOldValue($this->getOldValue())
+      ->setNewValue($this->getNewValue());
+
+    return $changes;
+  }
+
+
+}
diff --git a/src/xaction/class/SemiStructuredObjectTypeNameTransaction.php b/src/xaction/class/SemiStructuredObjectTypeNameTransaction.php
new file mode 100644
index 0000000..111239b
--- /dev/null
+++ b/src/xaction/class/SemiStructuredObjectTypeNameTransaction.php
@@ -0,0 +1,55 @@
+<?php
+
+final class SemiStructuredObjectTypeNameTransaction
+  extends SemiStructuredObjectTypeTransactionType {
+
+  const TRANSACTIONTYPE = 'semist:class:name';
+
+  public function generateOldValue($object) {
+    return $object->getName();
+  }
+
+  public function applyInternalEffects($object, $value) {
+    $object->setName($value);
+  }
+
+  public function getTitle() {
+    return pht(
+      '%s renamed this object type from %s to %s.',
+      $this->renderAuthor(),
+      $this->renderOldValue(),
+      $this->renderNewValue());
+  }
+
+  public function getTitleForFeed() {
+    return pht(
+      '%s renamed %s object type %s to %s.',
+      $this->renderAuthor(),
+      $this->renderObject(),
+      $this->renderOldValue(),
+      $this->renderNewValue());
+  }
+
+  public function validateTransactions($object, array $xactions) {
+    $errors = array();
+
+    if ($this->isEmptyTextTransaction($object->getName(), $xactions)) {
+      $errors[] = $this->newRequiredError(
+        pht('Object type must have a name.'));
+    }
+
+    $max_length = $object->getColumnMaximumByteLength('name');
+    foreach ($xactions as $xaction) {
+      $new_value = $xaction->getNewValue();
+      $new_length = strlen($new_value);
+      if ($new_length > $max_length) {
+        $errors[] = $this->newInvalidError(
+          pht('The name can be no longer than %s characters.',
+          new PhutilNumber($max_length)));
+      }
+    }
+
+    return $errors;
+  }
+
+}
diff --git a/src/xaction/class/SemiStructuredObjectTypeTransactionType.php b/src/xaction/class/SemiStructuredObjectTypeTransactionType.php
new file mode 100644
index 0000000..e6f6f48
--- /dev/null
+++ b/src/xaction/class/SemiStructuredObjectTypeTransactionType.php
@@ -0,0 +1,4 @@
+<?php
+
+abstract class SemiStructuredObjectTypeTransactionType
+  extends PhabricatorModularTransactionType {}