// when we get our next round of funding, let's figure out how to do this without a page reload
if(keys.publicKey.includes('/')){
console.log('TRYING AGAIN')
setTimeout(function(){
window.location.reload()
},10)
}else{
localforage.setItem('id',keys)
window.location.reload()
functionkeyPage(keys){
varscroller=document.getElementById('scroller')
varmessage=h('div',{classList:'message'})
message.appendChild(h('p',{innerHTML:marked('This is your ed25519 public/private keypair. It was generated using [Tweetnacl.js](https://tweetnacl.js.org/#/). Your public key is your identity when using [Bogbook](http://bogbook.com/), save your key in a safe place so that you can continue to use the same identity.')}))
vartextarea=h('textarea',{placeholder:'Write a new bog post'})
header.appendChild(textarea)
varcomposer=h('div',[
h('button',{
onclick:function(){
if(textarea.value){
varcontent={
author:keys.publicKey,
type:'post',
text:textarea.value,
timestamp:Date.now()
}
textarea.value=''
publish(content,keys)
}
}
},['Publish'])
])
header.appendChild(composer)
functionthreadPage(src,keys){
varscroller=document.getElementById('scroller')
localforage.getItem('log',function(err,log){
for(vari=log.length-1;i>=0;--i){
if(log[i].key===src){
varpost=log[i]
scroller.appendChild(renderMessage(post))
}
}
})
}
functionpublicPage(keys){
compose(keys)
localforage.getItem('log',function(err,log){
if(log){
for(vari=0;i<log.length;i++){
varpost=log[i]
scroller.appendChild(renderMessage(post))
}
varnewLog=log.sort(function(a,b){
returnb.content.timestamp-a.content.timestamp
})
if(newLog){
localforage.setItem('log',log)
}
}
})
}
functionroute(){
localforage.getItem('id',function(err,keys){
src=window.location.hash.substring(1)
varscroller=h('div',{id:'scroller'})
varscreen=document.getElementById('screen')
screen.appendChild(scroller)
if(src==='key'){
varkeyMessage=h('div',{classList:'message'})
keyMessage.appendChild(h('p',{innerHTML:marked('This is your ed25519 public/private keypair. It was generated using [Tweetnacl.js](https://tweetnacl.js.org/#/). Your public key is your identiy when using [Bogbook](http://bogbook.com/), save your key in a safe place so that you can continue to use the same identity.')}))
varnewPub=h('input',{placeholder:'Add a new pub. Ex: ws://bogbook.com/'})
varpubs=JSON.parse(localStorage['pubs'])
pubMessage.appendChild(h('div',[
h('p',{innerHTML:marked('These are your bogbook pubs. These servers will sync data when you publish a new post, when you subscribe to new feeds, and when you click on feed ids.')}),
newPub,
h('button',{
onclick:function(){
if(newPub.value){
pubs.push(newPub.value)
localStorage['pubs']=JSON.stringify(pubs)
location.reload()
}
}
},['Add Pub'])
]))
functionremoveButton(pubName){
varbutton=h('button',{
onclick:function(){
console.log('removing'+pubName)
for(vari=pubs.length;i--;){
if(pubs[i]===pubName){
pubs.splice(i,1);
localStorage['pubs']=JSON.stringify(pubs)
window.location.reload()
}
}
}
},['Remove Pub'])
returnbutton
}
if(keys.publicKey.includes('/')){
console.log('TRYING AGAIN')
setTimeout(function(){
location.reload()
},10)
}else{
varscroller=h('div',{id:'scroller'})
screen.appendChild(scroller)
for(i=0;i<pubs.length;i++){
varpubName=pubs[i]
pubMessage.appendChild(h('p',[
pubName,
removeButton(pubName)
]))
}
varmessage=h('div',{classList:'message'})
scroller.appendChild(pubMessage)
}
scroller.appendChild(message)
message.appendChild(h('h1',['Welcome to Bogbook']))
message.appendChild(h('p',['Bogbook is a distributed blogging network of signed append-only feeds. To avoid confusion, we call them "bogs".']))
elseif(src[0]==='@'){
varprofile=h('div',{classList:'message'})
scroller.appendChild(profile)
message.appendChild(h('p',['Please note: Bogbook is experimental software, not for use in producton environments. Expect bugs and breaking changes. Pull-requests are needed.']))
if(src==keys.publicKey){
varnameInput=h('input',{placeholder:'Publish a new name'})
message.appendChild(h('p',{innerHTML:marked('View the code: [http://github.com/bogbook/bog](http://github.com/bogbook/bog). Questions? [ev@evbogue.com](mailto:ev@evbogue.com).')}))
varnamePublisher=h('div',[
nameInput,
h('button',{
onclick:function(){
if(nameInput.value){
message.appendChild(h('hr'))
message.appendChild(h('h3',['Get started']))
varcontent={
author:keys.publicKey,
type:'name',
text:nameInput.value,
timestamp:Date.now()
}
message.appendChild(h('p',{innerHTML:marked('This is an ed25519 public/private signing keypair. It was generated using [TweetNaCl.js](https://tweetnacl.js.org/#/)')}))
message.appendChild(h('p',['Right now, this keypair exists only in memory. When you leave this page, the keypair will vanish forever. If you refresh this page you\'ll receive a new keypair.']))
profile.appendChild(namePublisher)
message.appendChild(h('p',{innerHTML:marked('To save this keypair, identify with handle below. Once you identify, your public/private keypair will be stored in your browser using [localForage.js](https://localforage.github.io/localForage). Save your keypair somewhere safe to preserve your identity.')}))
message.appendChild(h('p',['When you click [Identify], you will post your first message to your append-only bog, your ed25519 keypair will be saved in your browser, and the page will reload. Don\'t forget to back up your key! and happy bogging.']))
message.appendChild(h('hr'))
message.appendChild(h('h3',['Already have a key?']))
elseif(src[0]==='%'){
message.appendChild(h('p',['Import it here. Make sure to sync your existing feed from a Bogbook \'pub\' before posting a message.']))
localforage.getItem('log',function(err,log){
for(vari=log.length-1;i>=0;--i){
if(log[i].key===src){
varpost=log[i]
scroller.appendChild(renderMessage(post))
vartextarea=h('textarea',{placeholder:'Import your existing ed25519 keypair'})
@ -12,7 +12,7 @@ but nothing will load into the browser unless you request a public key, so try
### What?
bogbook is a distributed social networking application using [TweetNaCl.js](https://tweetnacl.js.org/#/) to publish signed append-only logs to your browser's localStorage.
bogbook is a distributed social networking application using [TweetNaCl.js](https://tweetnacl.js.org/#/) to publish signed append-only logs to your browser's IndexedDB using [localForage](https://localforage.github.io/localForage).
The bogs are then gossiped between your bog client and bog 'pub' servers using websockets. You're responsible for syncing your messages between different bog 'pub' servers. Bog 'pubs' themselves don't talk to each other, instead they only talk to clients.
@ -44,20 +44,31 @@ Bogbook should launch in your browser. If it doesn't, navigate to http://localho
All of the bogbook cryptography is produced using [TweetNaCl.js](https://tweetnacl.js.org/#/) which is a port of [TweetNaCl](https://tweetnacl.cr.yp.to/), a cryptography library written in 100 Tweets.
bogbook generates an ed25519 public/private keypair on load using `nacl.sign.keyPair()`, which is then stored in localStorage at `localStorage['id']` as a JSON object with the public/private keypairs base64-encoded.
bogbook generates an ed25519 public/private keypair on load using `nacl.sign.keyPair()`, which is then stored in localForage at `localStorage['id']` as a JSON object with the public/private keypairs base64-encoded.
When you post a new message, bogbook will
+ iterate up the message sequence number
+ hash the contents of the previous message using sha512
+ sign the contents of the current message with your ed25519 private key
+ hash the new message (including the signature) using sha512
+ generate a hash of the contents of the current message using sha512
+ generate a signature of the contents of the current message with your ed25519 private key
before appending the feed to the log stored in your browser.
Then you publish a message containing
```
{
"author": <yourpublickey>,
"key": <sha512hashofcontent>,
"signature": <signatureofcontent>
}
To view the message, you use `nacl.sign.open` passing Uint8Arrays of the signature and publickey as paramaters.
---
Please note: All logs are append-only, public, and plain text at the current time. While you _can_ moderate your local database and pub servers by deleting logs associated with public keys, it can be difficult to unsay something, so don't drink and bog, people.
Some browsers clear localStorage upon exit, others will clear it if you wipe your browser cache. Remember to save your public/private keypair somewhere, because no one can regenerate it for you.
Some browsers clear stored data upon exit, others will clear it if you wipe your browser cache. Remember to save your public/private keypair somewhere, because no one can regenerate it for you.