[tahoe-lafs-trac-stream] [tahoe-lafs] #510: use plain HTTP for storage server protocol
tahoe-lafs
trac at tahoe-lafs.org
Sun Oct 27 01:04:26 UTC 2013
#510: use plain HTTP for storage server protocol
------------------------------+---------------------------------
Reporter: warner | Owner: zooko
Type: enhancement | Status: new
Priority: major | Milestone: 2.0.0
Component: code-storage | Version: 1.2.0
Resolution: | Keywords: standards gsoc http
Launchpad Bug: |
------------------------------+---------------------------------
Comment (by simeon):
I've typed all this, but I'm getting tired again. Hopefully have
transferred the important core points now, and I will attempt sometime to
come back and clarify/summarise from this sketch. Let me know if you think
I am beating a different path, and that summary can be put somewhere else
where it won't clutter your system. :) For now, thanks for your thoughts
and good luck with your project.
Replying to [comment:32 simeon]:
> Replying to [comment:31 simeon]:
> > "...everything else done at a client end, apart from exchanging
objects between backend nodes and deprecation."
> >
> > Perhaps since deprecation is handled by the server, then
locking/blocking should also be
>
> Oh. And hashsumming of data, and making symlinks between a filename and
a hashsum db blob.
> I think the URI must be capable of specifying the following (question-
marks where I'm not sure the item is useful):
I forgot the salt! ;-) ... and the user ID for mutable files, which should
definitely be capable of being named the same as an immutable file in
every other respect, so that a user can easily create a file in their
directory with a name that they can see directly corresponds with a file
in the cache.
So adding salt we have
* optional salt
* ? file size or size-class ?
* ? checkdigit (a one-byte checksum of the hashsum, to detect user typos)
?
* hash algorithm (if omitted, default to a pre-configured value)
* truncatable hashsum (if omitted, query based on filename, if provided,
and if allowed by config)
* optional sequence number in case of hash collisions (not guaranteed to
be consistent between nodes, or across time, server maintained, and when
dups exist but one isn't specified, then return a list somehow to allow
the client to choose)
* optional filename
Salt has to be specified and stored where the server can find it easily,
(ie as part of the object label in my model) if the server checks
hashsums. If it does not, it could be hidden in client-only metadata, but
I think for users it's useful to keep it as an obvious part of the object
id ... we call the URI ... which they would see as 'filename'.
For usability I think this is too many fields, filesize and checkdigit
probably have to be dropped. There are two way to make the URI, either
strict field-ordering and using field-counting and hope the there is no
need to extend the schema later... or with explicit query-labels.
== Lots of examples to ponder ==
So what might a worst-case bungle of a compact usable and intuitive
implementation would look like? I'm just gonna use an example, cause I
forget how to do RFC syntax:
lafs:secretsalt;4k;XYZQabc1230;1;My+theory+by+Anne+Elque.html.gz
Where secretsalt is a custom salt; 4 is a single-byte file-size class; G
is a checkdigit for the complete hashsum; XYZQabc1230 is the first 11
chars of the hashsum, 1 is because the current db was DoSed with a
manufactured item that happens to hit the same hashsum as the file we
want; and the title is "My theory by Anne Elque".
With query labels, it goes something like
lafs:s=secretsalt;z=4;d=k;h=XYZQabc1230;f=My+theory+by+Anne+Elque;t=htmlgz
Or if we drop z and d, roll sequence-number into filename, and accept a
default salt, perhaps for the normal case where the item does not have
duplicate-hashsum siblings, we can use field counting to infer that the
salt is default, things look OK even with field identifiers
lafs:h=XYZQabc1230;f=My+theory+by+Anne+Elque.html.gz
lafs:s=secretsalt;h=XYZQabc1230;f=My+theory+by+Anne+Elque.html.gz
for an autmoated transaction, the server need not pass a filename (nor the
salt perhaps? depends on how we store items with different salts, and how
much time we want to spend doing lookups), so a request could just be for
lafs:h=XYZQabc1230
Want to save two bytes? Make the default field h=
lafs:XYZQabc1230
Users who are familiar might find this more convenient that a URI that
mentions a title.
If no h is provided, only an f, then optionally have the server do a
lookup. This could be optimised by keeping a set of mutable, possible non-
public files with reverse-lookup-mappings of filename to hashid.
Maybe the gz is a separate transform rather than a file extension, removed
when passing the object back to the user? Unsure which way is better, the
implications are not that important, but can become complex, eg a file
could be a gzip, stored by the db would it be gzipped again? Without a way
to specify so, then clients might do this. I don't think the server
backend should add gzip, but as discussed in earlier posting, client
should iff the content is compressible.
It's easy actually to test and compress if needed, so the client probably
should test, but not testing might make the SCHEMA simpler, since the
client would always assume it has/hasn't been gzipped. Not having a
separate field means counting on the file extension as being correct, and
this leads to problems if users misuse the file extension...
So this is a possible solution:
lafs:t=gz;s=secretsalt;h=XYZQabc1230;f=My+theory+by+Anne+Elque.html
on a web UI, this might be presented as
http://lafs-ui.com/htmlgz/secretsalt/XYZQabc1230;My+theory+by+Anne+Elque
and in the user's directory
http://lafs-
ui.com/AElque at lafsmail.com/secretsalt/XYZQabc1230;My+theory+by+Anne+Elque
or probably they either use the default salt, or have a personal default
configured somewhere in a client, so it can be shorter, like
http://lafs-ui.com/AElque@lafsmail.com/XYZQabc1230;My+theory+by+Anne+Elque
Does the lafs schema need to support user ids?
lafs:u=AElque at lafsmail.com;t=gz;s=secretsalt;h=XYZQabc1230;f=My+theory+by+Anne+Elque.html
Personally I think the email is preferrable to a PGP ID, easier for a user
to understand. But if using an ID, I guess this might be
lafs:u=0xA72B89345;t=gz;s=secretsalt;h=XYZQabc1230;f=My+theory+by+Anne+Elque.html
I'm probably misusing the request here, not sure how you intend to expose
the user directory. I'm guessing it makes sense only for mutable files, so
the h= and s= go away, if the user wants to represent them, they are
stored in the f= field,
lafs:u=AElque at lafsmail.com;t=gz;f=My+theory+by+Anne+Elque.html
lafs:u=AElque at lafsmail.com;t=gz;f=s=secretsalt;h=XYZQabc1230;f=My+theory+by+Anne+Elque.html
Are we confused yet? ;-) I'm guessing it doesn't make sense to allow the
schema to reference the mutable files, or else they need a specific
schema. Certainly in any case, the user-definable field of the 'filename'
needs to either be defined as potentially including strings that duplicate
syntax elements, or the server does need logic to disallow or escape such
entities. Myself, I prefer to say, filename always comes last, and can
include any legal HTTP URI filename field characters. But a simple other
way is to ensure that semi-colon is escaped within the field. I'm not
gonna re-write the above URIs that way though, you have to use your
imagination.
http://lafs-ui.com/AElque@lafsmail.com/XYZQabc1230;My+theory+by+Anne+Elque
http://lafs-ui.com/AElque@lafsmail.com/My+theory+by+Anne+Elque
Both of the above might be symlinks to the cache object item XYZQabc1230.
The user might specifically include the truncated hashsum that would be
seen on the cache item, but it's not needed because their homedir is a
bunch of links/redirects to cache objects. Or is that too insecure? Again,
my thoughts revolve around a public editing system, yours around a private
data store. In this case, I would want to adopt the semantics that you
would use.
I fear that having differing representations in web UI is
counterproductive, so it's best if the syntax can be as brief, compact and
intuitive and filename-friendly as possible. :)
Is it better then, to omit the (pretend) directory paths, and use fields
instead? I think this is uglier, less clear, so which is actually better,
ambiguity in the name of clarity?
http://lafs-
ui.com/u=AElque at lafsmail.com;t=gz;h=XYZQabc1230;f=My+theory+by+Anne+Elque
I like the directory paths for some parameters, fields for others. Perhaps
directory paths are OK where they would not be preserved in a save-file
anyway? That means maybe more like this:
http://lafs-ui.com/AElque@lafsmail.com/My+theory+by+Anne+Elque
http://lafs-ui.com/gzhtml/h=XYZQabc1230;f=My+theory+by+Anne+Elque
Two views of the same object, with the 'client' being a web UI on the site
lafs-ui.com, and one version being returned with mime-type html using HTTP
header "Content-Encoding: gzip", the second returned using mime-type
octet-stream and without the compression-encoding header, so the web
browser would provide the user the raw blob.
http://lafs-ui.com/gzhtml/h=XYZQabc1230
http://lafs-ui.com/cache/h=XYZQabc1230
Do a lookup for files by name?
http://lafs-ui.com/search/f=My+theory+by+Anne+Elque
When the user presses 'save file', it's gonna depend on the context of
their client how much of this gets passed through. I guess this means the
server sets the Content-Disposition filename field to something
comprehensive, and the client software filters it if the user prefers.
Users who use things like lynx or telnet to grab a file have to wittle the
fields down themselves. It's harder (unreliable) to reconstruct a field
unless the server provides it, so I doubt that it's worth saving bytes by
omitting or making the suggested filename minimalist. But perhaps it can
be taken on context, return filled with fields the client mentioned? Then
an internode-style transmission by hashsum-only has very little overhead,
the entire field can be omitted.
In a user client, it should be configurable, to anything from the full
lafs:blah down to just the filename portion, "My theory by Anne Elque",
but as mentioned, the client shouldn't try to reconstruct fields that the
server has not described, since that is not going to be easy to make
correct in every case.
== On disk ==
On disk on the server, the files can be stored efficiently using the
hashsum to distribute the objects equally across a bunch of
subdirectories. This is the real benefit of using the hash in the
filename, as I guess you were aware at least tangentially, since I guess
you understand the load-balancing aspect wrt distributed nodes. But it
makes it easy on the filesystem to do look-ups as well.
The server node makes a translation when the client requests hashsum
XYZQblah, to X/Y/XYZblah. Or for a larger installation, more
subdirectories, perhaps XY/ZQ/blah, or X/Y/Z/XYZQblah. This is hidden from
the user, and unless they are locally storing massive quantities of
unsorted files in their homedir, they wouldn't want to or need to emulate.
So I'm guessing if custom salts are allowed that could be implemented on
the server path as a root directory or parent directory tree of a similar
type ... specifics would depend on how many users, how many files?
And on the server the full hash should be stored in the filename.
Optionally it would be nice but not important to enable the user to
configure to see the full hash in the 'save as' dialogue too.
Human-readable portion of filename I think is made using a link to that
original object, resolved internally by the server to avoid excessive lag,
with appended desired/suggested filename field. POSIX hardlinks vs
symlinks vs hackish text file redirects have differing implications for
ease-of-management, cross-platform compatibility, and for consistency wrt
can one version disappear, be locked or blocked, while another remain.
Ultimately it might be nice to have the different mechanisms as
configurable policy options, otherwise the most sensible approach is to
take the simplest path I guess.
== The other fields ==
Below I've ranted a bit about the various fields that ultimately above I
decided were too problematic to bother the user with. This may be
interesting if you want to understand why, and it may be worth having the
API support the ideas, perhaps they have uses behind the scenes or in
special use-cases.
Another reason might be to help keep the schema overall compatible with
RFC 6920, even if it involves some translation that can be automated at
least in one direction (it's obviously not trivial to reconstruct a
hashsum in a URI that has been truncated to a length that 6920 doesn't
support, for example). I don't want to even think about it right now.
Here is my analysis, also don't want to revisit this right now, so it may
have errors and stuff.
It's only in the case of a hash collision with differing content that the
sequence number field would get used. Probably this option could be left
out of an implementation, but it's good I think to plan for perhaps
supporting it, in case an efficient mechanism is found for this DoS attack
vector. (My gaze hovers over the bitcoin generators at this point.)
The file-size classifier byte is basically for the same purpose. I see
bitcoin spawning methods for quickly generating massive numbers of hashes,
which who knows, some people might be able to leverage to use as a library
for hash collisions. (They use hash256 but can I think fairly easily
change.) But these items are of a small, perhaps even fixed size I think.
Including a filesize classification byte MAY be a way to get such hash-
collisions to not matter. On the other hand, I am not that sure it's worth
it, and dunno if others have done a rigorous analysis of such a strategy.
Checkdigit is for the human-typo factor. If the checkdigit does not match
then the user can be alerted. However ... it's only able to be checked
when the full hash is known, or else it would vary depending on the
truncation length of the hashsum. And since it can't easily be verified by
hand anyway, I'm again not all that sure if it's useful enough to be worth
using. If the bitspace of the hashsum truncation length is sufficient,
there should not be two items who are close enough together to be only a
mere typo apart. So a dud request leads to a lookup in either case, and an
error in either case.
Only if the client end is given the full hashsum by the user typing it in,
does the benefit arise, where eg a js could alert the user prior to the
client making the query to the server. I think this is useless, really. :)
On the other hand, if you did implement things that way, it might be
considered a life-saver by the poor human!
The usage I envisage would primarily be people copy-pasting URIs, or using
phones to capture QRCode style links, more often than they would manually
type one in. The main reason for truncating the hashsum is not to make it
easier to type, but to make it easier to read! Humans stop reading after a
lot of incomprehensible cruft. ;-) I hope this is not one of those cases.
;-) The URI has to fit neatly into the address bar, including enough of
the actual human-generated filename for the reader to notice that it has
one.
So I think checkdigit and filesize are important options for the URI
schema to support, but maybe not essential to a given implementation.
--
Ticket URL: <https://tahoe-lafs.org/trac/tahoe-lafs/ticket/510#comment:33>
tahoe-lafs <https://tahoe-lafs.org>
secure decentralized storage
More information about the tahoe-lafs-trac-stream
mailing list