OverviewFilesCommitsIssuesDocsReleasesPeers

apds commits / 11b8efb

rebrand bog5 server as APDS
11b8efbbd2aeb5fda24f27a5bc20004db847d4db
by Everett Bogue <ev@evbogue.com> · 2025-09-24T12:20:24.000Z

Changed files (6) +83 -137

README.md +3 -44
……@@ -1,49 +1,8 @@
1# Bog5 Protocol
1# APDS
22
3The 5th Bogbook protocol implementation.
3A personal data server (pds) for ANProto https://anproto.com/
44
5Try it at https://bog5.deno.dev/
6
7Please note the above link is not networked. For a fuller and networked version be sure to check out https://wiredove.net/
8
9### The Protocol
10
11Bogbook has been thru a few iterations. For the fans, thanks for sticking with me. For the people who want standards, I solicit your feedback. And now, the protocol...
12
13We send around sha256 hashes since we're using content-addressable storage for everything. First we request the hash that finds us a **protocol message**:
14
15```
16<ed25519 pubkey><ed25519 sig>
17```
18
19This opens to:
20
21```
22<unix timestamp><sha256 hash>
23```
24
25The timestamp is required since this is a social network. The sha256 hashlinks either locally or over the gossip network to a **content message**, which is Yaml file that contains everything else we need to make a post render while not duplicating too much of what we might have already such as avatar photos.
26
27Everything in the **protocol message** is we need to sort and authenticate a feed. Everything in the **content message** is what we need to render a message.
28
29```
30Text content
31```
32
33
34A full post might look like this:
35
36```
37---
38previous: sha256 hash previous protocol message
39name: Ev
40image: sha256 of image blob
41edit: sha256 hash of edited protocol message
42---
43Content
44```
45
46This means you could in theory edit your name and/or image on a post! But we also want a record of what your name and image was on the original post, since it is a security issue with some actors on decentralized social networks if you allow publishers to change their name over their entire feed. Or, for example, in a hack it would be harder to rewrite history.
5Try it at https://pds.anproto.com/
476
487---
498MIT
bogbot.js +50 -64
……@@ -1,8 +1,8 @@
1import nacl from './lib/nacl-fast-es.js'
2import { decode, encode } from './lib/base64.js'
1import { decode } from './lib/base64.js'
32import { cachekv } from './lib/cachekv.js'
43import { human } from './lib/human.js'
54import { vb } from './lib/vb.js'
5import { an } from 'https://esm.sh/gh/evbogue/anproto@f7cd761/an.js'
66
77let db
88let hashLog = []
……@@ -10,9 +10,9 @@ let openedLog = []
1010let newMessages = false
1111let sort = true
1212
13export const bogbot = {}
13export const apds = {}
1414
15bogbot.start = async (appId) => {
15apds.start = async (appId) => {
1616 db = await cachekv(appId)
1717
1818 setInterval(async () => {
……@@ -40,11 +40,11 @@ bogbot.start = async (appId) => {
4040 try {
4141 const obj = {
4242 hash,
43 sig: await bogbot.get(hash)
43 sig: await apds.get(hash)
4444 }
4545 obj.author = obj.sig.substring(0, 44)
46 obj.opened = await bogbot.open(obj.sig)
47 obj.text = await bogbot.get(obj.opened.substring(13))
46 obj.opened = await apds.open(obj.sig)
47 obj.text = await apds.get(obj.opened.substring(13))
4848 obj.ts = obj.opened.substring(0, 13)
4949 newArray.push(obj)
5050 } catch (err) { console.log(err) }
……@@ -65,71 +65,57 @@ bogbot.start = async (appId) => {
6565 }, 20000)
6666}
6767
68bogbot.generate = async () => {
69 const genkey = nacl.sign.keyPair()
70 const keygen = encode(genkey.publicKey) + encode(genkey.secretKey)
71 return keygen
68apds.generate = async () => {
69 const genkey = await an.gen()
70 return genkey
7271}
7372
74bogbot.keypair = async () => {
73apds.keypair = async () => {
7574 const keypair = await db.get('keypair')
7675 if (keypair) {
7776 return keypair
7877 }
7978}
8079
81bogbot.pubkey = async () => {
82 const keypair = await bogbot.keypair()
80apds.pubkey = async () => {
81 const keypair = await apds.keypair()
82 console.log(keypair)
8383 if (keypair) {
8484 return keypair.substring(0, 44)
8585 }
8686}
8787
88bogbot.privkey = async () => {
89 const keypair = await bogbot.keypair()
88apds.privkey = async () => {
89 const keypair = await apds.keypair()
9090 if (keypair) {
9191 return keypair.substring(44)
9292 }
9393}
9494
95bogbot.deletekey = async () => {
95apds.deletekey = async () => {
9696 db.rm('keypair')
9797}
9898
99bogbot.clear = async () => {
99apds.clear = async () => {
100100 db.clear()
101101}
102102
103bogbot.hash = async (data) => {
104 return encode(
105 Array.from(
106 new Uint8Array(
107 await crypto.subtle.digest("SHA-256", new TextEncoder().encode(data))
108 )
109 )
110 )
111}
112
113bogbot.sign = async (data) => {
114 const timestamp = Date.now()
103apds.hash = async (data) => { return await an.hash(data) }
115104
116 const hash = await bogbot.make(data)
105apds.sign = async (data) => {
106 const hash = await apds.make(data)
107 const sig = await an.sign(hash, await apds.keypair())
108 console.log(sig)
109 await apds.add(sig)
110 const protocolMsg = await apds.make(sig)
117111
118 const sig = encode(nacl.sign(new TextEncoder().encode(timestamp + hash), decode(await bogbot.privkey())))
119 await bogbot.add(await bogbot.pubkey() + sig)
120 const protocolMsg = await bogbot.make(await bogbot.pubkey() + sig)
121112 db.put('previous', protocolMsg)
122113 return protocolMsg
123114}
124115
125bogbot.open = async (msg) => {
116apds.open = async (msg) => {
126117 try {
127 const pubkey = msg.substring(0, 44)
128 const sig = msg.substring(44)
129
130 const opened = new TextDecoder().decode(nacl.sign.open(decode(sig), decode(pubkey)))
131
132 return opened
118 return await an.open(msg)
133119 } catch (err) {
134120 //console.log('Not a valid Bog5 protocol message')
135121 }
……@@ -137,15 +123,15 @@ bogbot.open = async (msg) => {
137123
138124import { yaml } from './lib/yaml.js'
139125
140bogbot.parseYaml = async (doc) => {
126apds.parseYaml = async (doc) => {
141127 return await yaml.parse(doc)
142128}
143129
144bogbot.createYaml = async (obj, content) => {
130apds.createYaml = async (obj, content) => {
145131 return await yaml.create(obj, content)
146132}
147133
148bogbot.compose = async (content, prev) => {
134apds.compose = async (content, prev) => {
149135 let obj = {}
150136 if (prev) { obj = prev }
151137
……@@ -158,39 +144,39 @@ bogbot.compose = async (content, prev) => {
158144 if (previous) { obj.previous = previous}
159145
160146 if (Object.keys(obj).length > 0) {
161 const yaml = await bogbot.createYaml(obj, content)
162 return await bogbot.sign(yaml)
147 const yaml = await apds.createYaml(obj, content)
148 return await apds.sign(yaml)
163149 } else {
164 return await bogbot.sign(content)
150 return await apds.sign(content)
165151 }
166152}
167153
168bogbot.make = async (data) => {
169 const hash = await bogbot.hash(data)
154apds.make = async (data) => {
155 const hash = await apds.hash(data)
170156
171157 await db.put(hash, data)
172158
173159 return hash
174160}
175161
176bogbot.get = async (hash) => {
162apds.get = async (hash) => {
177163 const blob = await db.get(hash)
178164
179165 return blob
180166}
181167
182bogbot.put = async (key, value) => {
168apds.put = async (key, value) => {
183169 await db.put(key, value)
184170}
185171
186bogbot.rm = async (key) => {
172apds.rm = async (key) => {
187173 await db.rm(key)
188174}
189175
190bogbot.add = async (msg) => {
191 const opened = await bogbot.open(msg)
176apds.add = async (msg) => {
177 const opened = await apds.open(msg)
192178 if (opened) {
193 const hash = await bogbot.make(msg)
179 const hash = await apds.make(msg)
194180 if (!hashLog.includes(hash)) {
195181 hashLog.push(hash)
196182 const obj = {
……@@ -199,7 +185,7 @@ bogbot.add = async (msg) => {
199185 }
200186 obj.author = obj.sig.substring(0, 44)
201187 obj.opened = opened
202 obj.text = await bogbot.get(obj.opened.substring(13))
188 obj.text = await apds.get(obj.opened.substring(13))
203189 obj.ts = obj.opened.substring(0, 13)
204190 openedLog.push(obj)
205191 newMessages = true
……@@ -208,11 +194,11 @@ bogbot.add = async (msg) => {
208194 }
209195}
210196
211bogbot.getHashLog = async () => { return hashLog }
197apds.getHashLog = async () => { return hashLog }
212198
213bogbot.getOpenedLog = async () => { return openedLog }
199apds.getOpenedLog = async () => { return openedLog }
214200
215bogbot.query = async (query) => {
201apds.query = async (query) => {
216202 if (openedLog[0] && !query) { return openedLog }
217203 if (openedLog[0] && query.startsWith('?')) {
218204 const search = query.substring(1).replace(/%20/g, ' ').toUpperCase()
……@@ -224,8 +210,8 @@ bogbot.query = async (query) => {
224210 }
225211}
226212
227bogbot.getPubkeys = async () => {
228 const arr = await bogbot.query()
213apds.getPubkeys = async () => {
214 const arr = await apds.query()
229215 const newSet = new Set()
230216 for (const msg of arr) {
231217 newSet.add(msg.author)
……@@ -234,16 +220,16 @@ bogbot.getPubkeys = async () => {
234220 return newArr
235221}
236222
237bogbot.getLatest = async (pubkey) => {
223apds.getLatest = async (pubkey) => {
238224 const q = openedLog.filter(msg => msg.author === pubkey)
239225 return q[q.length -1]
240226}
241227
242bogbot.human = async (ts) => {
228apds.human = async (ts) => {
243229 return await human(new Date(parseInt(ts)))
244230}
245231
246bogbot.visual = async (pubkey) => {
232apds.visual = async (pubkey) => {
247233 return vb(decode(pubkey), 256)
248234}
249235
composer.js +2 -2
……@@ -1,4 +1,4 @@
1import { bogbot } from './bogbot.js'
1import { apds } from './apds.js'
22import { render } from './render.js'
33
44export const composer = async () => {
……@@ -14,7 +14,7 @@ export const composer = async () => {
1414 b.textContent = 'Sign'
1515
1616 b.onclick = async () => {
17 const published = await bogbot.compose(ta.value)
17 const published = await apds.compose(ta.value)
1818 ta.value = ''
1919 const scroller = document.getElementById('scroller')
2020 await render.hash(published, scroller)
example.js +7 -6
……@@ -1,13 +1,14 @@
1import { bogbot } from './bogbot.js'
1import { apds } from './apds.js'
22import { profile } from './profile.js'
33import { composer } from './composer.js'
44import { render } from './render.js'
55
6await bogbot.start('bog5example')
6await apds.start('apds1')
77
8if (!await bogbot.pubkey()) {
9 const keypair = await bogbot.generate()
10 await bogbot.put('keypair', keypair)
8if (!await apds.pubkey()) {
9 const keypair = await apds.generate()
10 console.log(keypair)
11 await apds.put('keypair', keypair)
1112}
1213
1314document.body.appendChild(await profile())
……@@ -18,7 +19,7 @@ scroller.id = 'scroller'
1819
1920document.body.appendChild(scroller)
2021
21const log = await bogbot.query()
22const log = await apds.query()
2223
2324log.forEach(async (obj) => {
2425 await render.hash(obj.hash, scroller)
profile.js +11 -11
……@@ -1,14 +1,14 @@
11import { h } from './lib/h.js'
2import { bogbot } from './bogbot.js'
2import { apds } from './apds.js'
33
44export const profile = async () => {
55 const div = h('div')
66
7 const avatarImg = await bogbot.visual(await bogbot.pubkey())
7 const avatarImg = await apds.visual(await apds.pubkey())
88
9 const existingImage = await bogbot.get('image')
9 const existingImage = await apds.get('image')
1010
11 if (existingImage) { avatarImg.src = await bogbot.get(existingImage)}
11 if (existingImage) { avatarImg.src = await apds.get(existingImage)}
1212
1313 avatarImg.style = 'height: 30px; width: 30px; float: left; margin-right: 5px; object-fit: cover;'
1414
……@@ -46,13 +46,13 @@ export const profile = async () => {
4646 ctx.drawImage(img, 0, 0, width, height, 0, 0, cropWidth, cropHeight)
4747 const croppedImage = canvas.toDataURL()
4848 avatarImg.src = croppedImage
49 const hash = await bogbot.make(croppedImage)
50 await bogbot.put('image', hash)
49 const hash = await apds.make(croppedImage)
50 await apds.put('image', hash)
5151 } else {
5252 const croppedImage = canvas.toDataURL()
5353 avatarImg.src = img.src
54 const hash = await bogbot.make(img.src)
55 await bogbot.put('image', hash)
54 const hash = await apds.make(img.src)
55 await apds.put('image', hash)
5656 }
5757 }
5858 img.src = e.target.result
……@@ -65,9 +65,9 @@ export const profile = async () => {
6565
6666 div.appendChild(avatarImg)
6767
68 div.appendChild(h('div', [await bogbot.pubkey()]))
68 div.appendChild(h('div', [await apds.pubkey()]))
6969
70 const name = await bogbot.get('name')
70 const name = await apds.get('name')
7171
7272 const namer = h('input', {
7373 placeholder: name || 'Name yourself'
……@@ -79,7 +79,7 @@ export const profile = async () => {
7979 h('button', {onclick: async () => {
8080 if (namer.value) {
8181 namer.placeholder = namer.value
82 await bogbot.put('name', namer.value)
82 await apds.put('name', namer.value)
8383 namer.value = ''
8484 }
8585 }}, ['Save'])
render.js +10 -10
……@@ -1,28 +1,28 @@
1import { bogbot } from './bogbot.js'
1import { apds } from './apds.js'
22import { h } from './lib/h.js'
33
44export const render = {}
55
66render.blob = async (blob) => {
7 const hash = await bogbot.hash(blob)
7 const hash = await apds.hash(blob)
88
99 const div = await document.getElementById(hash)
1010
1111 try {
12 const opened = await bogbot.open(blob)
13 const ts = h('span', [await bogbot.human(opened.substring(0, 13))])
12 const opened = await apds.open(blob)
13 const ts = h('span', [await apds.human(opened.substring(0, 13))])
1414 setInterval(async () => {
15 ts.textContent = await bogbot.human(opened.substring(0, 13))
15 ts.textContent = await apds.human(opened.substring(0, 13))
1616 }, 1000)
1717 if (div) {
18 const img = await bogbot.visual(blob.substring(0, 44))
18 const img = await apds.visual(blob.substring(0, 44))
1919 img.id = 'image'
2020 img.style = 'width: 30px; height: 30px; float: left; margin-right: 5px; object-fit: cover;'
2121 div.appendChild(img)
2222 div.appendChild(h('a', {href: '#' + blob.substring(0, 44), id: 'name'}, [blob.substring(0, 10)]))
2323 div.appendChild(h('a', {href: '#' + hash, style: 'float: right;'}, [ts]))
2424 div.appendChild(h('div', {id: opened.substring(13)}))
25 const content = await bogbot.get(opened.substring(13))
25 const content = await apds.get(opened.substring(13))
2626 if (content) {
2727 await render.blob(content)
2828 }
……@@ -31,7 +31,7 @@ render.blob = async (blob) => {
3131 }
3232 } catch (err) {
3333 console.log('Not a valid protocol message')
34 const yaml = await bogbot.parseYaml(blob)
34 const yaml = await apds.parseYaml(blob)
3535 if (div) {
3636 div.textContent = yaml.body
3737 div.parentNode.childNodes.forEach(async (node) => {
……@@ -39,7 +39,7 @@ render.blob = async (blob) => {
3939 node.textContent = yaml.name
4040 }
4141 if (yaml.image && node.id === 'image') {
42 const image = await bogbot.get(yaml.image)
42 const image = await apds.get(yaml.image)
4343 node.src = image
4444 }
4545 })
……@@ -53,7 +53,7 @@ render.hash = async (hash, scroller) => {
5353
5454 scroller.insertBefore(div, scroller.firstChild)
5555
56 const sig = await bogbot.get(hash)
56 const sig = await apds.get(hash)
5757
5858 if (sig) {
5959 await render.blob(sig)