I was thinking through how to do the link shortening thing and here's the best solution I think: use AES-128 CBC to encrypt the ship and content ID and make a URL with it, then decode such requests and test whether the given ship has permission for the given resource. If it does, 301 redirect to the actual resource and if it doesn't, 403 forbidden.
This way you can give every subscribed ship its own unique urls for files you've shared, and you can revoke or change them individually. You also don't need to actually store these unique urls because you can just construct them when you send out updates to each ship, cos AES is jetted and very fast.
The approach for subscriptions would be to have a unique subscription path for
each subscribed ship like /updates/[ship] or whatever so you can give the
correct unique urls to the correct ship
Here's a basic set of data structures the app could use:
|%
+$ id @
+$ file
$: title=@t
note=(unit @t)
url=@t
ext=(unit @t)
==
+$ perms
$: white=(set ship)
black=(set ship)
==
+$ user
$: rev=@
files=(set id)
==
+$ decoded
$: =ship
rev=@
=id
==
+$ secret [k=@ iv=@]
+$ host (unit @t)
+$ users (map ship user)
+$ public (set id)
+$ local (map id [=file =perms])
+$ remote (map ship (map id file))And state can look something like this:
+$ state-0 [%0 =secret =host =users =public =local =remote]The content $id can be a random 64-bit atom. The rev field for the $user
is also a 64-bit number and is used to salt the encrypted urls, so you can
change them for that user down the line if you want to by changing that number.
The $secret structure in state is a 128-bit key and initialisation vector for
the AES-128 CBC algo, to construct and decrypt the unique urls.
The unique url is made by concatenating [128-bit ship address][64-bit user revision number][64-bit content ID], encrypting it with ~(en cbca:aes:crypto k.secret iv.secret), and then encoding it in a cord in base32.
Here's how the encoding function might work:
++ encode
|= [=host =ship =user =id =file =secret]
^- (unit @t)
?~ host ~
=/ raw=@ (can 3 ~[8^id 8^rev.user 16^ship])
=/ hash=tape
%- (v-co:co 32)
(~(en cbca:aes:crypto k.secret iv.secret) raw)
=/ query=tape
?~ ext.file ""
"?ext={(trip u.ext.file)}"
`(crip "http://{(trip u.host)}/share/{hash}{query}")
::
++ sample-encode-args
:* host=[~ 'example.com']
ship=~tinnus-napbus
user=[0 ~]
id=9.595.219.608.286.410.566
file=['foo' ~ 'xxx' ~ 'txt']
secret=[0w3J.V~dU2.Um-6~.0TghF.MJLmv 0w1n.Yz4ZH.CBhuM.hdsft.LFkM~]
==In the dojo:
> (encode:test sample-encode-args:test)
[~ 'http://example.com/share/18gboma04mas2tg23tl0vvs6viesn598e6ccqo8ib5n6i0ol8bdh?ext=txt']
And here's the decoding that'll happen when it receives a GET request for that url:
++ sample-decode-args
:* url='http://example.com/share/18gboma04mas2tg23tl0vvs6viesn598e6ccqo8ib5n6i0ol8bdh?ext=.txt'
secret=[0w3J.V~dU2.Um-6~.0TghF.MJLmv 0w1n.Yz4ZH.CBhuM.hdsft.LFkM~]
==
::
++ decode
|= [url=@t =secret]
^- (unit decoded)
=/ purl=(unit purl:eyre) (de-purl:html url)
?~ purl ~
=/ =pork:eyre q.u.purl
?. ?=([@ @ ~] q.pork) ~
=/ hash=@ (rash i.t.q.pork vum:ag)
=/ raw=@ (~(de cbca:aes:crypto k.secret iv.secret) hash)
:^ ~
(cut 3 [16 16] raw)
(cut 3 [8 8] raw)
(end 6 raw)
--in the dojo:
> (decode:test sample-decode-args:test)
[~ [ship=~tinnus-napbus rev=0 id=9.595.219.608.286.410.566]]
Upon receiving the get request and successfully decoding, it can check that the given user has permission to access the file with the given content ID, and if it does, it can return a 301 redirect to the actual url.
Other ships will subscribe to your %filesharer, so you need to be able to figure out which files are shared with them, so you need a map of ships to files for that, otherwise you'd have to iterate over all files to check which should be shared and there'd be some other complications too I think. Conversely, in the front end, you want to be able to share/unshare a file with particular users, so the front-end needs a map from file to users (so you can see file A is shared with ship X, Y and Z). So
$usersand$localcover these cases respectively. When you whitelisted/blacklisted a user for a particular file you'd need to update both of these so they're in sync.The
$publicstructure I was thinking was for files that you just shared with everybody, so anyone who subscribed to your %filesharer would receive these. I think you'd still wanna do the encoding thing for them cos you'd want to be able to revoke permissions later, it's really just necessary cos otherwise you'd have to add billions of ships to the whitelist.Also the
$localand$remotecases are for your files vs files others have shared with you.You can change up this stuff if it doesn't suit btw, I was just sketching out roughly what you'd need off the top of my head, it's not set in stone