diff --git a/README.md b/README.md
index b39fbe607bf6396f6a1c236919d64492cbcd9834..2bf04332c64b407f704dc3d8c4d33733cd60080d 100644
--- a/README.md
+++ b/README.md
@@ -219,12 +219,22 @@ Here's a list of things that I suspect will eventually roll themselves into bugs
 
 # ~scraaaatch -~ we have to re-write input / output / state items as genuine lists
 
-finish link w/ state, statechanges
-
 load program collections -> link w/ websocket
  - localize program load to view ? kinda wyld, but simplifies manager
  - do this for some key 'c' to load an assumed link->view->websocket to cf
 
+... ok, write ntlink, load on c
+ - link, w/ states, view, wsclient
+
+then scrape, to nautilus
+then natilus barebones link & manager & wsserver
+
+then serialization, links, your holy grail. if one could only say it and mean it
+
+scrape ?
+
+write the link code ... types -> serial types and back,
+
 wherever doth serialization work have to happen?
  - writedefinition, putdef respectively
  - ... all other manager requests -> write them down, minimize them, assemble from others
diff --git a/gogetter.js b/gogetter.js
index 6c2dadfe0d97d1ec52a0bc4f0242e76552814423..7f1493df9dbcc049af4374afd0067fe639b37c0b 100644
--- a/gogetter.js
+++ b/gogetter.js
@@ -106,7 +106,7 @@ function GoGetter() {
             		resolve(data)
             	},
             	error: (err) => {
-            		reject(new Error('failure at GG ajax get for json object'))
+            		reject(new Error("failure at GG ajax get for json object: program probably doesn't exist"))
             	}
             })
         })
diff --git a/hunks/link.js b/hunks/link.js
index ad9f8f382ed814d75f79b67b13e05dfb3f2f3df4..1d914a7adf3bf46eed5f64a5fa60cda92ad2646f 100644
--- a/hunks/link.js
+++ b/hunks/link.js
@@ -138,6 +138,7 @@ function Link() {
 
   this.loop = () => {
     // hurm
+    // ... 
   } // end loop
 }
 
diff --git a/hunks/manager.js b/hunks/manager.js
index 077e7802938882bbf2ec4ad062f28c082b5446d3..02b78abe8688d5775ee0b0746d2bfb8b2dd23bc4 100644
--- a/hunks/manager.js
+++ b/hunks/manager.js
@@ -503,7 +503,7 @@ function Manager() {
             // newhunks are automatically reported on msgs, and so are new links
             // so we just want a catch block here
           }).catch((err) => {
-            this.writemessage('error', 'ERR while adding program' + err)
+            this.writemessage('error', 'ERR while adding program: ' + err)
           })
           break
         case 'addhunk':
diff --git a/hunks/view.js b/hunks/view.js
index 1318b3102d71807fb527c25f0d5e3539adf0ef3b..00a92a6786bc6f035cc403389094648d4a8fa17b 100644
--- a/hunks/view.js
+++ b/hunks/view.js
@@ -108,10 +108,12 @@ function View() {
 
     // key listeners
     // keys are global, so ... idk how to unfoof this yet, but
-    if(this.id === 'TLView'){
+    if (this.id === 'TLView') {
       document.addEventListener('keydown', (evt) => {
-        if(evt.key === 'l'){
+        if (evt.key === 'l') {
           this.writemessage('addhunk', 'link')
+        } else if (evt.key === 'c') {
+          this.writemessage('addprogram', 'ntlink')
         }
       })
     }
@@ -251,6 +253,9 @@ function View() {
   // ok, my thoughts on this
   /*
 
+  when you're up with big programs, spend a day / a handful, just making the UI sing
+  - https://bl.ocks.org/mbostock/3750558
+
   at the moment this is kind of 'fine'
    - starting condition is mostly random (and elsewhere) - maybe some graph analysis
     - for who-is-generally-downstream-of-whomst
@@ -275,7 +280,7 @@ function View() {
   // happens when items added, deleted (changing topology)
   let updateForceLoop = () => {
     // init and/or update
-    if (!flsimrun && blocks.length > 1) {
+    if (!flsimrun && blocks.length > 3) {
       // Case for starting sim
       writeToMessageBox('starting force sim')
       flsimrun = true
@@ -283,14 +288,15 @@ function View() {
       let positions = this.getAllHunkPositions()
       let sizes = this.getAllHunkSizes()
       for (let i in positions) {
-        flnodes.push({
+        let nd = {
           index: i,
           x: positions[i].x,
           y: positions[i].y,
           vx: 0,
           vy: 0,
           r: sizes[i].width * sizemultiple
-        })
+        }
+        flnodes.push(nd)
       }
       flsim = d3.forceSimulation(flnodes)
         .force('charge', d3.forceManyBody().strength(250))
@@ -301,7 +307,7 @@ function View() {
         .alphaMin(finAlpha)
         .on('tick', flTick)
         .on('end', flEnd)
-    } else if (blocks.length <= 1) {
+    } else if (blocks.length <= 3) {
       // donot
     } else {
       // case for adding / rming from sim
@@ -311,7 +317,7 @@ function View() {
       if (positions.length > flnodes.length) {
         let last = positions.length - 1
         //console.log('to add new node like', positions[last])
-        let newNode = {
+        let nd = {
           index: last,
           x: positions[last].x,
           y: positions[last].y,
@@ -319,7 +325,7 @@ function View() {
           vy: 0,
           r: sizes[last].width * sizemultiple
         }
-        flnodes.push(newNode)
+        flnodes.push(nd)
         // console.log('SIM adds now this', newNode.x, newNode.y)
       } else {
         writeToMessageBox("SIM DELETE CASE NOT WRITTEN")
@@ -339,7 +345,7 @@ function View() {
 
   let flTick = () => {
     // called on sim update
-    let blks = $(this.plane).children('.block')
+    let blks = $(this.plane).children('.block').not('#NROL39_0').not('#TLView')
     if (blks.length !== flnodes.length) {
       console.log('FLOOP NODES MISMATCH', blks.length, flnodes.length)
     } else {
@@ -419,9 +425,13 @@ function View() {
     let nds = $(this.plane).children('.block')
     let positions = new Array()
     for (let nd of nds) {
-      let ps = readXY(nd)
-      ps.id = nd.id
-      positions.push(ps)
+      if ($(nd).attr('id') === "NROL39_0" || $(nd).attr('id') === "TLView") {
+        //console.log('skip')
+      } else {
+        let ps = readXY(nd)
+        ps.id = nd.id
+        positions.push(ps)
+      }
     }
     return positions
     // should do transform here ?
@@ -431,9 +441,13 @@ function View() {
     let nds = $(this.plane).children('.block')
     let sizes = new Array()
     for (let nd of nds) {
-      let ps = readSize(nd)
-      ps.id = nd.id
-      sizes.push(ps)
+      if ($(nd).attr('id') === "NROL39_0" || $(nd).attr('id') === "TLView") {
+        //console.log('skip')
+      } else {
+        let ps = readSize(nd)
+        ps.id = nd.id
+        sizes.push(ps)
+      }
     }
     return sizes
   }
@@ -568,6 +582,18 @@ function View() {
       $(match).remove()
     } else if (menu !== undefined) {
       mt = readTransform(menu)
+    } else if (def.id === "NROL39_0") {
+      mt = {
+        s: 1,
+        x: 100,
+        y: 100
+      }
+    } else if (def.id === "TLView") {
+      mt = {
+        s: 1,
+        x: 100,
+        y: 200
+      }
     } else {
       mt = {
         s: 1,
@@ -858,11 +884,12 @@ function View() {
   }
 
   let writeStateDom = (state, def) => {
-    let dom = $('<li>' + state.name + "(" + state.type + ")" + '</li>').get(0)
+    let dom = $('<li><p>' + state.name + " (" + state.type + ")" + '</p></li>').get(0)
     dom.id = def.id + '_state_' + state.name
     switch (typeof state.value) {
       case 'string':
-        let strinput = $('<input>').attr('type', 'text').attr('size', 24).attr('value', state.value).get(0)
+        //dom.append($('<br>').get(0))
+        let strinput = $('<input>').attr('type', 'text').attr('size', 32).attr('value', state.value).css('width', '240px').get(0)
         strinput.addEventListener('change', (evt) => {
           // ask for a change,
           // TODO HERE NOW: this is the state change request you want to write
diff --git a/programs/ntlink.json b/programs/ntlink.json
new file mode 100644
index 0000000000000000000000000000000000000000..6ddf7bf358d1899961f1e46535b980b8694abd41
--- /dev/null
+++ b/programs/ntlink.json
@@ -0,0 +1,48 @@
+{
+    "programname": "ntlink",
+    "hunks": [
+    {
+        "name": "link",
+        "id": "lnkone",
+        "state": {
+          "inputList": "msgs (byteArray), ipOne (uint32)",
+          "outputList": "msgs (byteArray), opOne (uint32)"
+        }
+    },
+    {
+        "name": "comm/websocketclient",
+        "id": "wsclient",
+        "state": {
+          "retrycount": 2
+        }
+    },
+    {
+        "name": "view",
+        "id": "nautilusview"
+    }],
+    "links": [
+    {
+        "outhunk": "lnkone",
+        "outname": "data",
+        "inhunk": "wsclient",
+        "inname": "data"
+    },
+    {
+        "outhunk": "wsclient",
+        "outname": "data",
+        "inhunk": "lnkone",
+        "inname": "data"
+    },
+    {
+        "outhunk": "nautilusview",
+        "outname": "msgs",
+        "inhunk": "lnkone",
+        "inname": "msgs"
+    },
+    {
+        "outhunk": "lnkone",
+        "outname": "msgs",
+        "inhunk": "nautilusview",
+        "inname": "msgs"
+    }]
+}