summaryrefslogtreecommitdiff
path: root/doc/disfluid.texi
blob: bf66908f33c29205e972d72bd58370b2ba3bdda4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
\input texinfo
@c -*-texinfo-*-

@c %**start of header
@setfilename disfluid.info
@documentencoding UTF-8
@settitle Demanding Interoperability to Strengthen the Free (Libre) Web: Introducing Disfluid
@c %**end of header

@set UPDATED 21 March 2023
@set UPDATED-MONTH March 2023
@include version_number.texi

@copying
Copyright @copyright{} 2023 Vivien Kraus

@quotation
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
any later version published by the Free Software Foundation; with no
Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.  A
copy of the license is included in the section entitled ``GNU Free
Documentation License''.
@end quotation
@end copying

@dircategory The Algorithmic Language Scheme
@direntry
* Disfluid: (disfluid)Decentralized Authentication on the Web
@end direntry

@titlepage
@title Interoperability to Strengthen the Free (Libre) Web: Introducing Disfluid
@author Vivien Kraus

@page
@vskip 0pt plus 1filll
Edition @value{EDITION} @*
@value{UPDATED} @*

@insertcopying
@end titlepage

@contents

@c *********************************************************************
@node Top
@top Disfluid

Disfluid is an independent implementation of a web stack focusing on
interoperability. In this implementation, the users control what
programs run in their computers. They also choose who to trust for
online data storage and processing, without needing any permission, or
can self-host their data.

The software is available at
@url{https://labo.planete-kraus.eu/disfluid.git}.

@menu
* Decentralized Authentication on the Web:: What is Disfluid?
* Running disfluid::
* The Disfluid Cache::
@end menu

@c *********************************************************************

@node Decentralized Authentication on the Web
@chapter Decentralized Authentication on the Web

Authentication on the web is currently handled in the following way:
anyone can install a server that will authenticate users on the
web. The problem is interoperability. If a client (an application)
wants to authenticate a user, it has to be approved by the
authentication server. In other words, if @var{useful-program} wants
to authenticate @var{MegaCorp} users, then @var{useful-program} has to
register to @var{MegaCorp} first, and get approved. This goes against
the principle of permission-less innovation, which is at the heart of
the web.

In the decentralized authentication web, the most successful attempt
so far is that of ActivityPub. All servers are interoperable with
respect to authentication: if user A emits an activity, it is
forwarded by A's server to its recipients, and A's server is
responsible for A's identity.

Another approach is that of Solid. While ActivityPub feels like
extensible e-mail over the (semantic) web, Solid aims to specify
everything, including what ActivityPub would call client-to-server, so
that the client applications can truly be decoupled from the
servers. Also Solid aims to have finer-grained interoperable
permissions and authorizations, and wants to integrate a full Linked
Data Platform, so that documents can be published directly on the
server instead of exchanged from server to server. Another feature of
Solid is to be fully usable from within the sandbox of a web browser,
which complicates the requirements.

Keeping all of this in mind, Disfluid wants to be a practical way to
enjoy the social network in an interoperable manner, where people
control the software they use.

@node Running disfluid
@chapter Running disfluid

@image{disfluid-main-window, 15cm}

Disfluid is packaged for the GNOME desktop as the Experiences
application. Experiences provides a main menu on the top right of the
screen.

@node The Disfluid Cache
@chapter The Disfluid Cache

@cindex cache
@cindex cache entry
Disfluid caches frequently-used data, to avoid overloading the
network. Each response and associated request are considered for cache
storage. They constitute a @dfn{cache entry}.

@cindex cache key
Each cache entry file stores some metadata, a @dfn{cache key}, the
response, and response body. To query the cache for an entry, the
@dfn{primary key} combines the HTTP method and effective URI. When the
response varies depending on a combination of headers, the values of
the relevant request headers are also considered as a @dfn{secondary
key}.

For instance, suppose that the following HTTP 1.1 request made over
https:
@example
GET /example HTTP/1.1
Host: example.com
Accept: text/plain
Foo: bar
Foo: other,thing
If-None-Match: W/"hello", "world"
If-None-Match: W/"hi :)"

@end example

has the following HTTP 1.1 response:

@example
HTTP/1.1 200 Hi
Content-Type: text/plain
ETag: W/\"hello\"
Vary: accept, foo
Vary: accept

Hi :)
@end example

Then the @dfn{cache key} is composed as:

@example
GET https://example.com/example
text/plain
bar,other,thing
text/plain
@end example

The first line of the cache key is the primary key. When querying the
cache, you should know the value. There may be different responses
that share the same primary key. You then have to examine the stored
response, and compose the list of varied header values, in order, to
narrow down the list of possible stored responses. Notice that you
must respect the order and multiplicity of all varied headers, and you
must aggregate all the values for duplicated request headers.

If the response has a Vary header for a request header that is not set
in the request, then the cache key has an empty corresponding line.

@deftypefun int disfluid_compute_cache_key (const char *@var{request_scheme}, const char *@var{request_header}, const char *@var{response_header}, size_t @var{max_key}, char *@var{key})
Write the cache key to @var{key}, up to @var{max_key}. The result is
NUL-terminated.

Return:

@table @strong
@item 0
on success;
@item -1
if the key cannot be computed due to an HTTP/1.1 error in the arguments;
@item -2
if the allocated @var{key} is too small to contain the whole key plus
a final NUL character.
@end table

@var{request_scheme}, @var{request_header}, @var{response_header} and
@var{key} must be non-overlapping.
@end deftypefun

@cindex cache entry metadata
Disfluid also store some metadata in the cache entry: the request and
response dates, according to the client, a boolean flag to mark an
entry as invalidated, and the HTTP scheme that was used (HTTP or
HTTPS).

@cindex hash, cache entry hash
If you have a method and URI to query, you can find all the relevant
entries by hashing the request method and URI.

@cindex private cache
When querying the cache for a @strong{private} cache implementation,
you must make sure to only consider entries that are available to the
party that is querying the cache. The method and URI are salted with
an optional password. If the password is not the empty string, then
only cache entries that have been added specifically with that
password will be available. If you are running a multi-user cache, you
can query the cache with the user name as the password in a private
setting, and then query the cache again with an empty password in a
shared setting, so you get all the private entries for that user, plus
all the shared entries.

@deftypefun int disfluid_hash_primary_cache_key (const char *@var{method}, const char *@var{uri}, const char *@var{password}, size_t @var{password_length}, size_t @var{max_hash}, char *@var{hash})
Hash @var{method} and @var{uri} to get a NUL-terminated hexadecimal
string of characters in @var{hash}. At most @var{max_hash} bytes will
be written. If @var{max_hash} is too short, then @var{hash} may not be
NUL-terminated, and the value @code{-2} is returned. Otherwise,
@code{0} is returned.
@end deftypefun

@deftp struct disfluid_cache_entry
A structure that can hold everything related to a cache entry. The
specific layout is hidden, but the overall size and alignment can be
queried at run-time.
@end deftp

@deftypefun size_t disfluid_cache_entry_size (size_t @var{max_key}, size_t @var{max_header}, size_t @var{max_body})
Return the size to allocate for a cache entry that can hold at most
@var{max_key} bytes of the key (method, URL and varied header values),
at most @var{max_header} for the response header, and at most
@var{max_body} for the response body. Do not hesitate to demand server
to send terse responses to you. 512 bytes for the key, 4 kB for the
header, and 2 MB for the body should be more than enough for proper
cache handling.

This function is a constant function according to GCC: its output only
depends on the three inputs, and it is not allowed to read global
data. The compiler is free to reuse the same value without callig the
function again. Define the @code{ATTRIBUTE_CONST} macro to the syntax
for function attributes for your compiler. If you use gnulib, use the
@samp{attribute} module and include @code{attribute.h} before
@code{disfluid.h} to use this optimization.
@end deftypefun

@deftypefun size_t disfluid_cache_entry_alignment (void)
Return the proper alignment for a cache entry. This function is
constant too.
@end deftypefun

@deftypefun size_t disfluid_cache_entry_init (struct disfluid_cache_entry *@var{entry}, size_t @var{max_key}, size_t @var{max_header}, size_t @var{max_body})
Initialize the @var{entry}, that must be aligned properly, and
allocated with an appropriate size, such as given by
@code{disfluid_cache_entry_size}.
@end deftypefun

@deftypefun void disfluid_cache_entry_minimum_size (const struct disfluid_cache_entry *@var{entry}, size_t *@var{min_key}, size_t *@var{min_header}, size_t *@var{min_body})
Set @var{min_key}, @var{min_header} and @var{min_body} to the minimum
sizes that should be allocated to hold the key, header and body of
@var{entry}.
@end deftypefun

@deftypefun int disfluid_cache_entry_copy (struct disfluid_cache_entry *@var{dest}, const struct disfluid_cache_entry *@var{src})
Copy @var{src} to @var{dest}. If one of the @var{dest} limits is too
short, return a negative error code. Otherwise, return 0.

@table @code
@item -1, -3, -5, -7
indicates that the key does not fit in @var{dest};
@item -2, -3, -6, -7
indicates that the response header does not fit in @var{dest};
@item -4, -5, -6, -7
indicates that the response body does not fit in @var{dest}.
@end table
@end deftypefun

@deftypefun {struct disfluid_cache_entry *} disfluid_cache_entry_alloc (size_t @var{max_key}, size_t @var{max_header}, size_t @var{max_body})
@deftypefunx {struct disfluid_cache_entry *} disfluid_cache_entry_dup (const struct disfluid_cache_entry *@var{src})
Allocate a new cache entry with the libc standard
allocator. @code{disfluid_cache_entry_dup} allocates the same limits
as those of @var{src}.
@end deftypefun

@deftypefun void disfluid_cache_entry_free (struct disfluid_cache_entry *@var{entry})
Free a cache entry allocated with the standard libc allocator.
@end deftypefun

To make memory allocation a bit safer, you can use the
@code{DISFLUID_CACHE_ENTRY_ALLOC (@var{ptr}, @var{max_key},
@var{max_header}, @var{max_body})} and
@code{DISFLUID_CACHE_ENTRY_FREE(@var{ptr})} macros as:

@example
struct disfluid_cache_entry *entry;
if (DISFLUID_CACHE_ENTRY_ALLOC (entry, 512, 4096, 2 * 1024 * 1024) < 0)
  @{
    /* Handle memory allocation failure */
  @}
else
  @{
    /* Do something with entry… */
  @}
DISFLUID_CACHE_ENTRY_FREE (entry);
/* entry is set to NULL */
assert (entry == NULL);
@end example

@deftypefun void disfluid_cache_entry_set_request_date (struct disfluid_cache_entry *@var{entry}, const struct timespec *@var{request_date})
@deftypefunx void disfluid_cache_entry_set_response_date (struct disfluid_cache_entry *@var{entry}, const struct timespec *@var{response_date})
@deftypefunx void disfluid_cache_entry_set_invalidated (struct disfluid_cache_entry *@var{entry}, int @var{invalidated})
@deftypefunx void disfluid_cache_entry_invalidate (struct disfluid_cache_entry *@var{entry})
Manage the metadata associated with @var{entry}.
@end deftypefun

@deftypefun int disfluid_cache_entry_set_key (struct disfluid_cache_entry *@var{entry}, const char *@var{key})
@deftypefunx int disfluid_cache_entry_set_header (struct disfluid_cache_entry *@var{entry}, const char *@var{header})
@deftypefunx int disfluid_cache_entry_set_body (struct disfluid_cache_entry *@var{entry}, size_t @var{body_length}, const char *@var{body})
Try and set the relevant cache entry section in @var{entry}. If the
respective limit is too short, return a negative error
code. Otherwise, return 0.
@end deftypefun

@deftypefun void disfluid_cache_entry_get_request_date (const struct disfluid_cache_entry *@var{entry}, struct timespec *@var{request_date})
@deftypefunx void disfluid_cache_entry_get_response_date (const struct disfluid_cache_entry *@var{entry}, struct timespec *@var{response_date})
@deftypefunx int disfluid_cache_entry_is_invalidated (const struct disfluid_cache_entry *@var{entry})
Query the metadata of @var{entry}.
@end deftypefun

@deftypefun size_t disfluid_cache_entry_get_key (const struct disfluid_cache_entry *@var{entry}, size_t @var{start}, size_t @var{max}, char *@var{dst})
@deftypefunx size_t disfluid_cache_entry_get_header (const struct disfluid_cache_entry *@var{entry}, size_t @var{start}, size_t @var{max}, char *@var{dst})
@deftypefunx size_t disfluid_cache_entry_get_body (const struct disfluid_cache_entry *@var{entry}, size_t @var{start}, size_t @var{max}, char *@var{dst})
Ignore the first @var{start} bytes, and copy the next bytes of the
relevant section in @var{entry}, up to @var{max}, to @var{dst}. Return
the total length of the section. If there is more room allocated, make
sure that @var{dst} is NUL-terminated.
@end deftypefun

@deftypefun int disfluid_cache_entry_load (struct disfluid_cache_entry *@var{entry}, int @var{load_key}, int @var{load_header}, int @var{load_body}, ssize_t (*@var{read_impl}) (void *, void *, size_t), int (*@var{skip}) (void *, size_t), void *@var{context})
@deftypefunx int disfluid_cache_entry_read (struct disfluid_cache_entry *@var{entry}, int @var{load_key}, int @var{load_header}, int @var{load_body}, int @var{fd})
@deftypefunx int disfluid_cache_entry_fread (struct disfluid_cache_entry *@var{entry}, int @var{load_key}, int @var{load_header}, int @var{load_body}, FILE *@var{handle})
Load a file to set up @var{entry}. If @var{load_key} (respectively,
@var{load_header} and @var{load_body}) is a strictly positive value,
then attempt to read the key (respectively, the header and body). If
it is 0, then skip it, but keep the previously loaded value. If it is
negative, then erase the value. The specific
@code{disfluid_cache_entry_read} and @code{disfluid_cache_entry_fread}
use a file descriptor and a stream file handle respectively, instead
of custom callbacks.

@code{disfluid_cache_entry_load} uses custom callbacks to read the
file. The file is passed as @var{context}, and during the calls to
@var{read_impl} and @var{skip}, it is bound to the first argument. The
second argument of @var{read_impl} is bound to a buffer where to read
the bytes. The last argument is bound to the read request size, in
bytes.

@var{read_impl} is supposed to return a negative value if a file
system failure occurs, 0 if the file is at end, or a value at most
equal to the read request, and set the bytes in the buffer
accordingly. @var{skip} is supposed to return 0 on success and advance
the file pointer by the request size, or return a negative error code.

The loading functions return 0 on success, -1 in case of a file system
error, -2 if the file is invalid, -3 if it is incomplete, -4 if the
key is too large, -5 if the header is too large, -6 if the body is too
large.
@end deftypefun

@deftypefun int disfluid_cache_entry_save (const struct disfluid_cache_entry *@var{entry}, ssize_t (*@var{write_impl}) (void *, const void *, size_t), void *@var{context})
@deftypefunx int disfluid_cache_entry_write (const struct disfluid_cache_entry *@var{entry}, int @var{fd})
@deftypefunx int disfluid_cache_entry_fwrite (const struct disfluid_cache_entry *@var{entry}, FILE *@var{handle})
Save @var{entry} to a file. Return 0 on success, -1 if writing to the
file raised an error, -2 if the key, header or body is unset in
@var{entry}. @var{write_impl} is called with @var{context} bound to
the first argument, an array of bytes to write as the second, and the
array length as the third. It should return a negative value to signal
an error, or a value at most the write request length, and write the
corresponding bytes in the request to file.
@end deftypefun

@bye