Fossil SCM

Merge in trunk latest developments.

andybradford 2019-08-31 16:23 test-updates merge
Commit 08291fec9f6f215bee8acbbbf5a7c8e4b5ee49a98ed493dcb0809ceb49a76f8b
--- skins/default/header.txt
+++ skins/default/header.txt
@@ -17,11 +17,11 @@
1717
html "<a href='$home$url' class='active $cls'>$name</a>\n"
1818
} else {
1919
html "<a href='$home$url' class='$cls'>$name</a>\n"
2020
}
2121
}
22
-html "<a id='hbbtn' href='#'>&#9776;</a>"
22
+html "<a id='hbbtn' href='/sitemap'>&#9776;</a>"
2323
menulink $index_page Home {}
2424
if {[anycap jor]} {
2525
menulink /timeline Timeline {}
2626
}
2727
if {[hascap oh]} {
2828
--- skins/default/header.txt
+++ skins/default/header.txt
@@ -17,11 +17,11 @@
17 html "<a href='$home$url' class='active $cls'>$name</a>\n"
18 } else {
19 html "<a href='$home$url' class='$cls'>$name</a>\n"
20 }
21 }
22 html "<a id='hbbtn' href='#'>&#9776;</a>"
23 menulink $index_page Home {}
24 if {[anycap jor]} {
25 menulink /timeline Timeline {}
26 }
27 if {[hascap oh]} {
28
--- skins/default/header.txt
+++ skins/default/header.txt
@@ -17,11 +17,11 @@
17 html "<a href='$home$url' class='active $cls'>$name</a>\n"
18 } else {
19 html "<a href='$home$url' class='$cls'>$name</a>\n"
20 }
21 }
22 html "<a id='hbbtn' href='/sitemap'>&#9776;</a>"
23 menulink $index_page Home {}
24 if {[anycap jor]} {
25 menulink /timeline Timeline {}
26 }
27 if {[hascap oh]} {
28
--- src/checkin.c
+++ src/checkin.c
@@ -1939,10 +1939,11 @@
19391939
/*
19401940
** COMMAND: ci*
19411941
** COMMAND: commit
19421942
**
19431943
** Usage: %fossil commit ?OPTIONS? ?FILE...?
1944
+** or: %fossil ci ?OPTIONS? ?FILE...?
19441945
**
19451946
** Create a new version containing all of the changes in the current
19461947
** checkout. You will be prompted to enter a check-in comment unless
19471948
** the comment has been specified on the command-line using "-m" or a
19481949
** file containing the comment using -M. The editor defined in the
19491950
--- src/checkin.c
+++ src/checkin.c
@@ -1939,10 +1939,11 @@
1939 /*
1940 ** COMMAND: ci*
1941 ** COMMAND: commit
1942 **
1943 ** Usage: %fossil commit ?OPTIONS? ?FILE...?
 
1944 **
1945 ** Create a new version containing all of the changes in the current
1946 ** checkout. You will be prompted to enter a check-in comment unless
1947 ** the comment has been specified on the command-line using "-m" or a
1948 ** file containing the comment using -M. The editor defined in the
1949
--- src/checkin.c
+++ src/checkin.c
@@ -1939,10 +1939,11 @@
1939 /*
1940 ** COMMAND: ci*
1941 ** COMMAND: commit
1942 **
1943 ** Usage: %fossil commit ?OPTIONS? ?FILE...?
1944 ** or: %fossil ci ?OPTIONS? ?FILE...?
1945 **
1946 ** Create a new version containing all of the changes in the current
1947 ** checkout. You will be prompted to enter a check-in comment unless
1948 ** the comment has been specified on the command-line using "-m" or a
1949 ** file containing the comment using -M. The editor defined in the
1950
+21 -7
--- src/db.c
+++ src/db.c
@@ -117,10 +117,11 @@
117117
/*
118118
** Arrange for the given file to be deleted on a failure.
119119
*/
120120
void db_delete_on_failure(const char *zFilename){
121121
assert( db.nDeleteOnFail<count(db.azDeleteOnFail) );
122
+ if( zFilename==0 ) return;
122123
db.azDeleteOnFail[db.nDeleteOnFail++] = fossil_strdup(zFilename);
123124
}
124125
125126
/*
126127
** Return the transaction nesting depth. 0 means we are currently
@@ -799,10 +800,13 @@
799800
}
800801
801802
/*
802803
** Initialize a new database file with the given schema. If anything
803804
** goes wrong, call db_err() to exit.
805
+**
806
+** If zFilename is NULL, then create an empty repository in an in-memory
807
+** database.
804808
*/
805809
void db_init_database(
806810
const char *zFileName, /* Name of database file to create */
807811
const char *zSchema, /* First part of schema */
808812
... /* Additional SQL to run. Terminate with NULL. */
@@ -810,11 +814,11 @@
810814
sqlite3 *db;
811815
int rc;
812816
const char *zSql;
813817
va_list ap;
814818
815
- db = db_open(zFileName);
819
+ db = db_open(zFileName ? zFileName : ":memory:");
816820
sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0);
817821
rc = sqlite3_exec(db, zSchema, 0, 0, 0);
818822
if( rc!=SQLITE_OK ){
819823
db_err("%s", sqlite3_errmsg(db));
820824
}
@@ -825,11 +829,15 @@
825829
db_err("%s", sqlite3_errmsg(db));
826830
}
827831
}
828832
va_end(ap);
829833
sqlite3_exec(db, "COMMIT", 0, 0, 0);
830
- sqlite3_close(db);
834
+ if( zFileName || g.db!=0 ){
835
+ sqlite3_close(db);
836
+ }else{
837
+ g.db = db;
838
+ }
831839
}
832840
833841
/*
834842
** Function to return the number of seconds since 1970. This is
835843
** the same as strftime('%s','now') but is more compact.
@@ -1768,12 +1776,13 @@
17681776
17691777
/*
17701778
** Flags for the db_find_and_open_repository() function.
17711779
*/
17721780
#if INTERFACE
1773
-#define OPEN_OK_NOT_FOUND 0x001 /* Do not error out if not found */
1774
-#define OPEN_ANY_SCHEMA 0x002 /* Do not error if schema is wrong */
1781
+#define OPEN_OK_NOT_FOUND 0x001 /* Do not error out if not found */
1782
+#define OPEN_ANY_SCHEMA 0x002 /* Do not error if schema is wrong */
1783
+#define OPEN_SUBSTITUTE 0x004 /* Fake in-memory repo if not found */
17751784
#endif
17761785
17771786
/*
17781787
** Try to find the repository and open it. Use the -R or --repository
17791788
** option to locate the repository. If no such option is available, then
@@ -1802,11 +1811,16 @@
18021811
if( g.repositoryOpen ){
18031812
if( (bFlags & OPEN_ANY_SCHEMA)==0 ) db_verify_schema();
18041813
return;
18051814
}
18061815
rep_not_found:
1807
- if( (bFlags & OPEN_OK_NOT_FOUND)==0 ){
1816
+ if( bFlags & OPEN_OK_NOT_FOUND ){
1817
+ /* No errors if the database is not found */
1818
+ if( bFlags & OPEN_SUBSTITUTE ){
1819
+ db_create_repository(0);
1820
+ }
1821
+ }else{
18081822
#ifdef FOSSIL_ENABLE_JSON
18091823
g.json.resultCode = FSL_JSON_E_DB_NOT_FOUND;
18101824
#endif
18111825
if( nArgUsed==0 ){
18121826
fossil_fatal("use --repository or -R to specify the repository database");
@@ -2032,12 +2046,12 @@
20322046
}
20332047
db_multi_exec(
20342048
"INSERT OR IGNORE INTO user(login, info) VALUES(%Q,'')", zUser
20352049
);
20362050
db_multi_exec(
2037
- "UPDATE user SET cap='s', pw=lower(hex(randomblob(3)))"
2038
- " WHERE login=%Q", zUser
2051
+ "UPDATE user SET cap='s', pw=%Q"
2052
+ " WHERE login=%Q", fossil_random_password(10), zUser
20392053
);
20402054
if( !setupUserOnly ){
20412055
db_multi_exec(
20422056
"INSERT OR IGNORE INTO user(login,pw,cap,info)"
20432057
" VALUES('anonymous',hex(randomblob(8)),'hmnc','Anon');"
20442058
--- src/db.c
+++ src/db.c
@@ -117,10 +117,11 @@
117 /*
118 ** Arrange for the given file to be deleted on a failure.
119 */
120 void db_delete_on_failure(const char *zFilename){
121 assert( db.nDeleteOnFail<count(db.azDeleteOnFail) );
 
122 db.azDeleteOnFail[db.nDeleteOnFail++] = fossil_strdup(zFilename);
123 }
124
125 /*
126 ** Return the transaction nesting depth. 0 means we are currently
@@ -799,10 +800,13 @@
799 }
800
801 /*
802 ** Initialize a new database file with the given schema. If anything
803 ** goes wrong, call db_err() to exit.
 
 
 
804 */
805 void db_init_database(
806 const char *zFileName, /* Name of database file to create */
807 const char *zSchema, /* First part of schema */
808 ... /* Additional SQL to run. Terminate with NULL. */
@@ -810,11 +814,11 @@
810 sqlite3 *db;
811 int rc;
812 const char *zSql;
813 va_list ap;
814
815 db = db_open(zFileName);
816 sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0);
817 rc = sqlite3_exec(db, zSchema, 0, 0, 0);
818 if( rc!=SQLITE_OK ){
819 db_err("%s", sqlite3_errmsg(db));
820 }
@@ -825,11 +829,15 @@
825 db_err("%s", sqlite3_errmsg(db));
826 }
827 }
828 va_end(ap);
829 sqlite3_exec(db, "COMMIT", 0, 0, 0);
830 sqlite3_close(db);
 
 
 
 
831 }
832
833 /*
834 ** Function to return the number of seconds since 1970. This is
835 ** the same as strftime('%s','now') but is more compact.
@@ -1768,12 +1776,13 @@
1768
1769 /*
1770 ** Flags for the db_find_and_open_repository() function.
1771 */
1772 #if INTERFACE
1773 #define OPEN_OK_NOT_FOUND 0x001 /* Do not error out if not found */
1774 #define OPEN_ANY_SCHEMA 0x002 /* Do not error if schema is wrong */
 
1775 #endif
1776
1777 /*
1778 ** Try to find the repository and open it. Use the -R or --repository
1779 ** option to locate the repository. If no such option is available, then
@@ -1802,11 +1811,16 @@
1802 if( g.repositoryOpen ){
1803 if( (bFlags & OPEN_ANY_SCHEMA)==0 ) db_verify_schema();
1804 return;
1805 }
1806 rep_not_found:
1807 if( (bFlags & OPEN_OK_NOT_FOUND)==0 ){
 
 
 
 
 
1808 #ifdef FOSSIL_ENABLE_JSON
1809 g.json.resultCode = FSL_JSON_E_DB_NOT_FOUND;
1810 #endif
1811 if( nArgUsed==0 ){
1812 fossil_fatal("use --repository or -R to specify the repository database");
@@ -2032,12 +2046,12 @@
2032 }
2033 db_multi_exec(
2034 "INSERT OR IGNORE INTO user(login, info) VALUES(%Q,'')", zUser
2035 );
2036 db_multi_exec(
2037 "UPDATE user SET cap='s', pw=lower(hex(randomblob(3)))"
2038 " WHERE login=%Q", zUser
2039 );
2040 if( !setupUserOnly ){
2041 db_multi_exec(
2042 "INSERT OR IGNORE INTO user(login,pw,cap,info)"
2043 " VALUES('anonymous',hex(randomblob(8)),'hmnc','Anon');"
2044
--- src/db.c
+++ src/db.c
@@ -117,10 +117,11 @@
117 /*
118 ** Arrange for the given file to be deleted on a failure.
119 */
120 void db_delete_on_failure(const char *zFilename){
121 assert( db.nDeleteOnFail<count(db.azDeleteOnFail) );
122 if( zFilename==0 ) return;
123 db.azDeleteOnFail[db.nDeleteOnFail++] = fossil_strdup(zFilename);
124 }
125
126 /*
127 ** Return the transaction nesting depth. 0 means we are currently
@@ -799,10 +800,13 @@
800 }
801
802 /*
803 ** Initialize a new database file with the given schema. If anything
804 ** goes wrong, call db_err() to exit.
805 **
806 ** If zFilename is NULL, then create an empty repository in an in-memory
807 ** database.
808 */
809 void db_init_database(
810 const char *zFileName, /* Name of database file to create */
811 const char *zSchema, /* First part of schema */
812 ... /* Additional SQL to run. Terminate with NULL. */
@@ -810,11 +814,11 @@
814 sqlite3 *db;
815 int rc;
816 const char *zSql;
817 va_list ap;
818
819 db = db_open(zFileName ? zFileName : ":memory:");
820 sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0);
821 rc = sqlite3_exec(db, zSchema, 0, 0, 0);
822 if( rc!=SQLITE_OK ){
823 db_err("%s", sqlite3_errmsg(db));
824 }
@@ -825,11 +829,15 @@
829 db_err("%s", sqlite3_errmsg(db));
830 }
831 }
832 va_end(ap);
833 sqlite3_exec(db, "COMMIT", 0, 0, 0);
834 if( zFileName || g.db!=0 ){
835 sqlite3_close(db);
836 }else{
837 g.db = db;
838 }
839 }
840
841 /*
842 ** Function to return the number of seconds since 1970. This is
843 ** the same as strftime('%s','now') but is more compact.
@@ -1768,12 +1776,13 @@
1776
1777 /*
1778 ** Flags for the db_find_and_open_repository() function.
1779 */
1780 #if INTERFACE
1781 #define OPEN_OK_NOT_FOUND 0x001 /* Do not error out if not found */
1782 #define OPEN_ANY_SCHEMA 0x002 /* Do not error if schema is wrong */
1783 #define OPEN_SUBSTITUTE 0x004 /* Fake in-memory repo if not found */
1784 #endif
1785
1786 /*
1787 ** Try to find the repository and open it. Use the -R or --repository
1788 ** option to locate the repository. If no such option is available, then
@@ -1802,11 +1811,16 @@
1811 if( g.repositoryOpen ){
1812 if( (bFlags & OPEN_ANY_SCHEMA)==0 ) db_verify_schema();
1813 return;
1814 }
1815 rep_not_found:
1816 if( bFlags & OPEN_OK_NOT_FOUND ){
1817 /* No errors if the database is not found */
1818 if( bFlags & OPEN_SUBSTITUTE ){
1819 db_create_repository(0);
1820 }
1821 }else{
1822 #ifdef FOSSIL_ENABLE_JSON
1823 g.json.resultCode = FSL_JSON_E_DB_NOT_FOUND;
1824 #endif
1825 if( nArgUsed==0 ){
1826 fossil_fatal("use --repository or -R to specify the repository database");
@@ -2032,12 +2046,12 @@
2046 }
2047 db_multi_exec(
2048 "INSERT OR IGNORE INTO user(login, info) VALUES(%Q,'')", zUser
2049 );
2050 db_multi_exec(
2051 "UPDATE user SET cap='s', pw=%Q"
2052 " WHERE login=%Q", fossil_random_password(10), zUser
2053 );
2054 if( !setupUserOnly ){
2055 db_multi_exec(
2056 "INSERT OR IGNORE INTO user(login,pw,cap,info)"
2057 " VALUES('anonymous',hex(randomblob(8)),'hmnc','Anon');"
2058
+2 -2
--- src/doc.c
+++ src/doc.c
@@ -526,12 +526,12 @@
526526
for(base=0, i=7; i<n; i++){
527527
if( z[i]=='$'
528528
&& strncmp(&z[i],"$ROOT/", 6)==0
529529
&& (z[i-1]=='\'' || z[i-1]=='"')
530530
&& i-base>=9
531
- && (fossil_strnicmp(&z[i-7]," href=", 6)==0 ||
532
- fossil_strnicmp(&z[i-9]," action=", 8)==0)
531
+ && ((fossil_strnicmp(&z[i-6],"href=",5)==0 && fossil_isspace(z[i-7])) ||
532
+ (fossil_strnicmp(&z[i-8],"action=",7)==0 && fossil_isspace(z[i-9])) )
533533
){
534534
blob_append(cgi_output_blob(), &z[base], i-base);
535535
blob_appendf(cgi_output_blob(), "%R");
536536
base = i+5;
537537
}
538538
--- src/doc.c
+++ src/doc.c
@@ -526,12 +526,12 @@
526 for(base=0, i=7; i<n; i++){
527 if( z[i]=='$'
528 && strncmp(&z[i],"$ROOT/", 6)==0
529 && (z[i-1]=='\'' || z[i-1]=='"')
530 && i-base>=9
531 && (fossil_strnicmp(&z[i-7]," href=", 6)==0 ||
532 fossil_strnicmp(&z[i-9]," action=", 8)==0)
533 ){
534 blob_append(cgi_output_blob(), &z[base], i-base);
535 blob_appendf(cgi_output_blob(), "%R");
536 base = i+5;
537 }
538
--- src/doc.c
+++ src/doc.c
@@ -526,12 +526,12 @@
526 for(base=0, i=7; i<n; i++){
527 if( z[i]=='$'
528 && strncmp(&z[i],"$ROOT/", 6)==0
529 && (z[i-1]=='\'' || z[i-1]=='"')
530 && i-base>=9
531 && ((fossil_strnicmp(&z[i-6],"href=",5)==0 && fossil_isspace(z[i-7])) ||
532 (fossil_strnicmp(&z[i-8],"action=",7)==0 && fossil_isspace(z[i-9])) )
533 ){
534 blob_append(cgi_output_blob(), &z[base], i-base);
535 blob_appendf(cgi_output_blob(), "%R");
536 base = i+5;
537 }
538
+1 -1
--- src/fshell.c
+++ src/fshell.c
@@ -109,11 +109,11 @@
109109
continue;
110110
}
111111
if( childPid==0 ){
112112
/* This is the child process */
113113
int main(int, char**);
114
- main(nArg, azArg);
114
+ fossil_main(nArg, azArg);
115115
exit(0);
116116
}else{
117117
/* The parent process */
118118
int status;
119119
waitpid(childPid, &status, 0);
120120
--- src/fshell.c
+++ src/fshell.c
@@ -109,11 +109,11 @@
109 continue;
110 }
111 if( childPid==0 ){
112 /* This is the child process */
113 int main(int, char**);
114 main(nArg, azArg);
115 exit(0);
116 }else{
117 /* The parent process */
118 int status;
119 waitpid(childPid, &status, 0);
120
--- src/fshell.c
+++ src/fshell.c
@@ -109,11 +109,11 @@
109 continue;
110 }
111 if( childPid==0 ){
112 /* This is the child process */
113 int main(int, char**);
114 fossil_main(nArg, azArg);
115 exit(0);
116 }else{
117 /* The parent process */
118 int status;
119 waitpid(childPid, &status, 0);
120
+2 -2
--- src/login.c
+++ src/login.c
@@ -2018,11 +2018,11 @@
20182018
const char *zNewName = find_option("name",0,1);
20192019
const char *zOther;
20202020
char *zErr = 0;
20212021
verify_all_options();
20222022
if( g.argc!=4 ){
2023
- fossil_fatal("unknown extra arguments to \"login-group add\"");
2023
+ fossil_fatal("unknown extra arguments to \"login-group join\"");
20242024
}
20252025
zOther = g.argv[3];
20262026
login_group_leave(&zErr);
20272027
sqlite3_free(zErr);
20282028
zErr = 0;
@@ -2042,11 +2042,11 @@
20422042
login_group_leave(&zErr);
20432043
if( zErr ) fossil_fatal("Oops: %s", zErr);
20442044
return;
20452045
}
20462046
}else{
2047
- fossil_fatal("unknown command \"%s\" - should be \"add\" or \"leave\"",
2047
+ fossil_fatal("unknown command \"%s\" - should be \"join\" or \"leave\"",
20482048
zCmd);
20492049
}
20502050
}
20512051
/* Show the current login group information */
20522052
zLGName = login_group_name();
20532053
--- src/login.c
+++ src/login.c
@@ -2018,11 +2018,11 @@
2018 const char *zNewName = find_option("name",0,1);
2019 const char *zOther;
2020 char *zErr = 0;
2021 verify_all_options();
2022 if( g.argc!=4 ){
2023 fossil_fatal("unknown extra arguments to \"login-group add\"");
2024 }
2025 zOther = g.argv[3];
2026 login_group_leave(&zErr);
2027 sqlite3_free(zErr);
2028 zErr = 0;
@@ -2042,11 +2042,11 @@
2042 login_group_leave(&zErr);
2043 if( zErr ) fossil_fatal("Oops: %s", zErr);
2044 return;
2045 }
2046 }else{
2047 fossil_fatal("unknown command \"%s\" - should be \"add\" or \"leave\"",
2048 zCmd);
2049 }
2050 }
2051 /* Show the current login group information */
2052 zLGName = login_group_name();
2053
--- src/login.c
+++ src/login.c
@@ -2018,11 +2018,11 @@
2018 const char *zNewName = find_option("name",0,1);
2019 const char *zOther;
2020 char *zErr = 0;
2021 verify_all_options();
2022 if( g.argc!=4 ){
2023 fossil_fatal("unknown extra arguments to \"login-group join\"");
2024 }
2025 zOther = g.argv[3];
2026 login_group_leave(&zErr);
2027 sqlite3_free(zErr);
2028 zErr = 0;
@@ -2042,11 +2042,11 @@
2042 login_group_leave(&zErr);
2043 if( zErr ) fossil_fatal("Oops: %s", zErr);
2044 return;
2045 }
2046 }else{
2047 fossil_fatal("unknown command \"%s\" - should be \"join\" or \"leave\"",
2048 zCmd);
2049 }
2050 }
2051 /* Show the current login group information */
2052 zLGName = login_group_name();
2053
+11
--- src/main.c
+++ src/main.c
@@ -328,10 +328,12 @@
328328
/*
329329
** atexit() handler which frees up "some" of the resources
330330
** used by fossil.
331331
*/
332332
static void fossil_atexit(void) {
333
+ static int once = 0;
334
+ if( once++ ) return; /* Ensure that this routine only runs once */
333335
#if USE_SEE
334336
/*
335337
** Zero, unlock, and free the saved database encryption key now.
336338
*/
337339
db_unsave_encryption_key();
@@ -630,10 +632,19 @@
630632
int _CRT_glob = 0x0001; /* See MinGW bug #2062 */
631633
#endif
632634
int main(int argc, char **argv)
633635
#endif
634636
{
637
+ return fossil_main(argc, argv);
638
+}
639
+
640
+/* All the work of main() is done by a separate procedure "fossil_main()".
641
+** We have to break this out, because fossil_main() is sometimes called
642
+** separately (by the "shell" command) but we do not want atwait() handlers
643
+** being called by separate invocations of fossil_main().
644
+*/
645
+int fossil_main(int argc, char **argv){
635646
const char *zCmdName = "unknown";
636647
const CmdOrPage *pCmd = 0;
637648
int rc;
638649
639650
#if !defined(_WIN32_WCE)
640651
--- src/main.c
+++ src/main.c
@@ -328,10 +328,12 @@
328 /*
329 ** atexit() handler which frees up "some" of the resources
330 ** used by fossil.
331 */
332 static void fossil_atexit(void) {
 
 
333 #if USE_SEE
334 /*
335 ** Zero, unlock, and free the saved database encryption key now.
336 */
337 db_unsave_encryption_key();
@@ -630,10 +632,19 @@
630 int _CRT_glob = 0x0001; /* See MinGW bug #2062 */
631 #endif
632 int main(int argc, char **argv)
633 #endif
634 {
 
 
 
 
 
 
 
 
 
635 const char *zCmdName = "unknown";
636 const CmdOrPage *pCmd = 0;
637 int rc;
638
639 #if !defined(_WIN32_WCE)
640
--- src/main.c
+++ src/main.c
@@ -328,10 +328,12 @@
328 /*
329 ** atexit() handler which frees up "some" of the resources
330 ** used by fossil.
331 */
332 static void fossil_atexit(void) {
333 static int once = 0;
334 if( once++ ) return; /* Ensure that this routine only runs once */
335 #if USE_SEE
336 /*
337 ** Zero, unlock, and free the saved database encryption key now.
338 */
339 db_unsave_encryption_key();
@@ -630,10 +632,19 @@
632 int _CRT_glob = 0x0001; /* See MinGW bug #2062 */
633 #endif
634 int main(int argc, char **argv)
635 #endif
636 {
637 return fossil_main(argc, argv);
638 }
639
640 /* All the work of main() is done by a separate procedure "fossil_main()".
641 ** We have to break this out, because fossil_main() is sometimes called
642 ** separately (by the "shell" command) but we do not want atwait() handlers
643 ** being called by separate invocations of fossil_main().
644 */
645 int fossil_main(int argc, char **argv){
646 const char *zCmdName = "unknown";
647 const CmdOrPage *pCmd = 0;
648 int rc;
649
650 #if !defined(_WIN32_WCE)
651
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -433,11 +433,15 @@
433433
char zClose[20];
434434
435435
if( zLink==0 || zLink[0]==0 ){
436436
zClose[0] = 0;
437437
}else{
438
- wiki_resolve_hyperlink(ob, 0, zLink, zClose, sizeof(zClose), 0, zTitle);
438
+ static const int flags =
439
+ WIKI_NOBADLINKS |
440
+ WIKI_MARKDOWNLINKS
441
+ ;
442
+ wiki_resolve_hyperlink(ob, flags, zLink, zClose, sizeof(zClose), 0, zTitle);
439443
}
440444
if( blob_size(content)==0 ){
441445
if( link ) BLOB_APPEND_BLOB(ob, link);
442446
}else{
443447
BLOB_APPEND_BLOB(ob, content);
444448
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -433,11 +433,15 @@
433 char zClose[20];
434
435 if( zLink==0 || zLink[0]==0 ){
436 zClose[0] = 0;
437 }else{
438 wiki_resolve_hyperlink(ob, 0, zLink, zClose, sizeof(zClose), 0, zTitle);
 
 
 
 
439 }
440 if( blob_size(content)==0 ){
441 if( link ) BLOB_APPEND_BLOB(ob, link);
442 }else{
443 BLOB_APPEND_BLOB(ob, content);
444
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -433,11 +433,15 @@
433 char zClose[20];
434
435 if( zLink==0 || zLink[0]==0 ){
436 zClose[0] = 0;
437 }else{
438 static const int flags =
439 WIKI_NOBADLINKS |
440 WIKI_MARKDOWNLINKS
441 ;
442 wiki_resolve_hyperlink(ob, flags, zLink, zClose, sizeof(zClose), 0, zTitle);
443 }
444 if( blob_size(content)==0 ){
445 if( link ) BLOB_APPEND_BLOB(ob, link);
446 }else{
447 BLOB_APPEND_BLOB(ob, content);
448
+53
--- src/util.c
+++ src/util.c
@@ -525,5 +525,58 @@
525525
fossil_panic("pledge(\"%s\",NULL) fails with errno=%d",
526526
promises, (int)errno);
527527
}
528528
}
529529
#endif /* defined(HAVE_PLEDGE) */
530
+
531
+/*
532
+** Construct a random password and return it as a string. N is the
533
+** recommended number of characters for the password.
534
+**
535
+** Space to hold the returned string is obtained from fossil_malloc()
536
+** and should be freed by the caller.
537
+*/
538
+char *fossil_random_password(int N){
539
+ char zSrc[60];
540
+ int nSrc;
541
+ int i;
542
+ char z[60];
543
+
544
+ /* Source characters for the password. Omit characters like "0", "O",
545
+ ** "1" and "I" that might be easily confused */
546
+ static const char zAlphabet[] =
547
+ /* 0 1 2 3 4 5 */
548
+ /* 123456789 123456789 123456789 123456789 123456789 123456 */
549
+ "23456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
550
+
551
+ if( N<8 ) N = 8;
552
+ else if( N>sizeof(zAlphabet)-2 ) N = sizeof(zAlphabet)-2;
553
+ nSrc = sizeof(zAlphabet) - 1;
554
+ memcpy(zSrc, zAlphabet, nSrc);
555
+
556
+ for(i=0; i<N; i++){
557
+ unsigned r;
558
+ sqlite3_randomness(sizeof(r), &r);
559
+ r %= nSrc;
560
+ z[i] = zSrc[r];
561
+ zSrc[r] = zSrc[--nSrc];
562
+ }
563
+ z[i] = 0;
564
+ return fossil_strdup(z);
565
+}
566
+
567
+/*
568
+** COMMAND: test-random-password
569
+**
570
+** Usage: %fossil test-random-password ?N?
571
+**
572
+** Generate a random password string of approximately N characters in length.
573
+** If N is omitted, use 10. Values of N less than 8 are changed to 8
574
+** and greater than 55 and changed to 55.
575
+*/
576
+void test_random_password(void){
577
+ int N = 10;
578
+ if( g.argc>=3 ){
579
+ N = atoi(g.argv[2]);
580
+ }
581
+ fossil_print("%s\n", fossil_random_password(N));
582
+}
530583
--- src/util.c
+++ src/util.c
@@ -525,5 +525,58 @@
525 fossil_panic("pledge(\"%s\",NULL) fails with errno=%d",
526 promises, (int)errno);
527 }
528 }
529 #endif /* defined(HAVE_PLEDGE) */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
530
--- src/util.c
+++ src/util.c
@@ -525,5 +525,58 @@
525 fossil_panic("pledge(\"%s\",NULL) fails with errno=%d",
526 promises, (int)errno);
527 }
528 }
529 #endif /* defined(HAVE_PLEDGE) */
530
531 /*
532 ** Construct a random password and return it as a string. N is the
533 ** recommended number of characters for the password.
534 **
535 ** Space to hold the returned string is obtained from fossil_malloc()
536 ** and should be freed by the caller.
537 */
538 char *fossil_random_password(int N){
539 char zSrc[60];
540 int nSrc;
541 int i;
542 char z[60];
543
544 /* Source characters for the password. Omit characters like "0", "O",
545 ** "1" and "I" that might be easily confused */
546 static const char zAlphabet[] =
547 /* 0 1 2 3 4 5 */
548 /* 123456789 123456789 123456789 123456789 123456789 123456 */
549 "23456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
550
551 if( N<8 ) N = 8;
552 else if( N>sizeof(zAlphabet)-2 ) N = sizeof(zAlphabet)-2;
553 nSrc = sizeof(zAlphabet) - 1;
554 memcpy(zSrc, zAlphabet, nSrc);
555
556 for(i=0; i<N; i++){
557 unsigned r;
558 sqlite3_randomness(sizeof(r), &r);
559 r %= nSrc;
560 z[i] = zSrc[r];
561 zSrc[r] = zSrc[--nSrc];
562 }
563 z[i] = 0;
564 return fossil_strdup(z);
565 }
566
567 /*
568 ** COMMAND: test-random-password
569 **
570 ** Usage: %fossil test-random-password ?N?
571 **
572 ** Generate a random password string of approximately N characters in length.
573 ** If N is omitted, use 10. Values of N less than 8 are changed to 8
574 ** and greater than 55 and changed to 55.
575 */
576 void test_random_password(void){
577 int N = 10;
578 if( g.argc>=3 ){
579 N = atoi(g.argv[2]);
580 }
581 fossil_print("%s\n", fossil_random_password(N));
582 }
583
+1
--- src/wiki.c
+++ src/wiki.c
@@ -1629,10 +1629,11 @@
16291629
** Render markdown wiki from FILE to stdout.
16301630
**
16311631
*/
16321632
void test_markdown_render(void){
16331633
Blob in, out;
1634
+ db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
16341635
verify_all_options();
16351636
if( g.argc!=3 ) usage("FILE");
16361637
blob_zero(&out);
16371638
blob_read_from_file(&in, g.argv[2], ExtFILE);
16381639
markdown_to_html(&in, 0, &out);
16391640
--- src/wiki.c
+++ src/wiki.c
@@ -1629,10 +1629,11 @@
1629 ** Render markdown wiki from FILE to stdout.
1630 **
1631 */
1632 void test_markdown_render(void){
1633 Blob in, out;
 
1634 verify_all_options();
1635 if( g.argc!=3 ) usage("FILE");
1636 blob_zero(&out);
1637 blob_read_from_file(&in, g.argv[2], ExtFILE);
1638 markdown_to_html(&in, 0, &out);
1639
--- src/wiki.c
+++ src/wiki.c
@@ -1629,10 +1629,11 @@
1629 ** Render markdown wiki from FILE to stdout.
1630 **
1631 */
1632 void test_markdown_render(void){
1633 Blob in, out;
1634 db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
1635 verify_all_options();
1636 if( g.argc!=3 ) usage("FILE");
1637 blob_zero(&out);
1638 blob_read_from_file(&in, g.argv[2], ExtFILE);
1639 markdown_to_html(&in, 0, &out);
1640
+24 -19
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -30,10 +30,11 @@
3030
#define WIKI_NOBLOCK 0x004 /* No block markup of any kind */
3131
#define WIKI_BUTTONS 0x008 /* Allow sub-menu buttons */
3232
#define WIKI_NOBADLINKS 0x010 /* Ignore broken hyperlinks */
3333
#define WIKI_LINKSONLY 0x020 /* No markup. Only decorate links */
3434
#define WIKI_NEWLINE 0x040 /* Honor \n - break lines at each \n */
35
+#define WIKI_MARKDOWNLINKS 0x080 /* Resolve hyperlinks as in markdown */
3536
#endif
3637
3738
3839
/*
3940
** These are the only markup attributes allowed.
@@ -1211,20 +1212,22 @@
12111212
** [http://www.fossil-scm.org/]
12121213
** [https://www.fossil-scm.org/]
12131214
** [ftp://www.fossil-scm.org/]
12141215
** [mailto:[email protected]]
12151216
**
1216
-** [/path]
1217
+** [/path] -> Refers to the root of the Fossil hierarchy, not
1218
+** the root of the URI domain
12171219
**
12181220
** [./relpath]
1221
+** [../relpath]
12191222
**
1220
-** [WikiPageName]
1221
-** [wiki:WikiPageName]
1223
+** [#fragment]
12221224
**
12231225
** [0123456789abcdef]
12241226
**
1225
-** [#fragment]
1227
+** [WikiPageName]
1228
+** [wiki:WikiPageName]
12261229
**
12271230
** [2010-02-27 07:13]
12281231
*/
12291232
void wiki_resolve_hyperlink(
12301233
Blob *pOut, /* Write the HTML output here */
@@ -1296,24 +1299,35 @@
12961299
blob_appendf(pOut, "%z[",xhref(zExtraNS, "%R/info/%s", zTarget));
12971300
zTerm = "]</a>";
12981301
}else{
12991302
zTerm = "";
13001303
}
1301
- }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'
1302
- && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){
1303
- blob_appendf(pOut, "<a href=\"%R/timeline?c=%T\"%s>", zTarget, zExtra);
13041304
}else if( (z = validWikiPageName(mFlags, zTarget))!=0 ){
1305
+ /* The link is to a valid wiki page name */
13051306
const char *zOverride = wiki_is_overridden(zTarget);
13061307
if( zOverride ){
13071308
blob_appendf(pOut, "<a href=\"%R/info/%S\"%s>", zOverride, zExtra);
13081309
}else{
13091310
blob_appendf(pOut, "<a href=\"%R/wiki?name=%T\"%s>", z, zExtra);
13101311
}
1311
- }else if( zOrig && zTarget>=&zOrig[2] && !fossil_isspace(zTarget[-2]) ){
1312
- /* Probably an array subscript in code */
1312
+ }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'
1313
+ && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){
1314
+ /* Dates or date-and-times in ISO8610 resolve to a link to the
1315
+ ** timeline for that date */
1316
+ blob_appendf(pOut, "<a href=\"%R/timeline?c=%T\"%s>", zTarget, zExtra);
1317
+ }else if( mFlags & WIKI_MARKDOWNLINKS ){
1318
+ /* If none of the above, and if rendering links for markdown, then
1319
+ ** create a link to the literal text of the target */
1320
+ blob_appendf(pOut, "<a href=\"%h\"%s>", zTarget, zExtra);
1321
+ }else if( zOrig && zTarget>=&zOrig[2]
1322
+ && zTarget[-1]=='[' && !fossil_isspace(zTarget[-2]) ){
1323
+ /* If the hyperlink markup is not preceded by whitespace, then it
1324
+ ** is probably a C-language subscript or similar, not really a
1325
+ ** hyperlink. Just ignore it. */
13131326
zTerm = "";
13141327
}else if( (mFlags & (WIKI_NOBADLINKS|WIKI_LINKSONLY))!=0 ){
1328
+ /* Also ignore the link if various flags are set */
13151329
zTerm = "";
13161330
}else{
13171331
blob_appendf(pOut, "<span class=\"brokenlink\">[%h]", zTarget);
13181332
zTerm = "</span>";
13191333
}
@@ -1758,20 +1772,10 @@
17581772
}
17591773
blob_append(renderer.pOut, "\n", 1);
17601774
free(renderer.aStack);
17611775
}
17621776
1763
-/*
1764
-** Send a string as wiki to CGI output.
1765
-*/
1766
-void wiki_write(const char *zIn, int flags){
1767
- Blob in;
1768
- blob_init(&in, zIn, -1);
1769
- wiki_convert(&in, 0, flags);
1770
- blob_reset(&in);
1771
-}
1772
-
17731777
/*
17741778
** COMMAND: test-wiki-render
17751779
**
17761780
** Usage: %fossil test-wiki-render FILE [OPTIONS]
17771781
**
@@ -1790,10 +1794,11 @@
17901794
if( find_option("htmlonly",0,0)!=0 ) flags |= WIKI_HTMLONLY;
17911795
if( find_option("linksonly",0,0)!=0 ) flags |= WIKI_LINKSONLY;
17921796
if( find_option("nobadlinks",0,0)!=0 ) flags |= WIKI_NOBADLINKS;
17931797
if( find_option("inline",0,0)!=0 ) flags |= WIKI_INLINE;
17941798
if( find_option("noblock",0,0)!=0 ) flags |= WIKI_NOBLOCK;
1799
+ db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
17951800
verify_all_options();
17961801
if( g.argc!=3 ) usage("FILE");
17971802
blob_zero(&out);
17981803
blob_read_from_file(&in, g.argv[2], ExtFILE);
17991804
wiki_convert(&in, &out, flags);
18001805
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -30,10 +30,11 @@
30 #define WIKI_NOBLOCK 0x004 /* No block markup of any kind */
31 #define WIKI_BUTTONS 0x008 /* Allow sub-menu buttons */
32 #define WIKI_NOBADLINKS 0x010 /* Ignore broken hyperlinks */
33 #define WIKI_LINKSONLY 0x020 /* No markup. Only decorate links */
34 #define WIKI_NEWLINE 0x040 /* Honor \n - break lines at each \n */
 
35 #endif
36
37
38 /*
39 ** These are the only markup attributes allowed.
@@ -1211,20 +1212,22 @@
1211 ** [http://www.fossil-scm.org/]
1212 ** [https://www.fossil-scm.org/]
1213 ** [ftp://www.fossil-scm.org/]
1214 ** [mailto:[email protected]]
1215 **
1216 ** [/path]
 
1217 **
1218 ** [./relpath]
 
1219 **
1220 ** [WikiPageName]
1221 ** [wiki:WikiPageName]
1222 **
1223 ** [0123456789abcdef]
1224 **
1225 ** [#fragment]
 
1226 **
1227 ** [2010-02-27 07:13]
1228 */
1229 void wiki_resolve_hyperlink(
1230 Blob *pOut, /* Write the HTML output here */
@@ -1296,24 +1299,35 @@
1296 blob_appendf(pOut, "%z[",xhref(zExtraNS, "%R/info/%s", zTarget));
1297 zTerm = "]</a>";
1298 }else{
1299 zTerm = "";
1300 }
1301 }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'
1302 && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){
1303 blob_appendf(pOut, "<a href=\"%R/timeline?c=%T\"%s>", zTarget, zExtra);
1304 }else if( (z = validWikiPageName(mFlags, zTarget))!=0 ){
 
1305 const char *zOverride = wiki_is_overridden(zTarget);
1306 if( zOverride ){
1307 blob_appendf(pOut, "<a href=\"%R/info/%S\"%s>", zOverride, zExtra);
1308 }else{
1309 blob_appendf(pOut, "<a href=\"%R/wiki?name=%T\"%s>", z, zExtra);
1310 }
1311 }else if( zOrig && zTarget>=&zOrig[2] && !fossil_isspace(zTarget[-2]) ){
1312 /* Probably an array subscript in code */
 
 
 
 
 
 
 
 
 
 
 
 
1313 zTerm = "";
1314 }else if( (mFlags & (WIKI_NOBADLINKS|WIKI_LINKSONLY))!=0 ){
 
1315 zTerm = "";
1316 }else{
1317 blob_appendf(pOut, "<span class=\"brokenlink\">[%h]", zTarget);
1318 zTerm = "</span>";
1319 }
@@ -1758,20 +1772,10 @@
1758 }
1759 blob_append(renderer.pOut, "\n", 1);
1760 free(renderer.aStack);
1761 }
1762
1763 /*
1764 ** Send a string as wiki to CGI output.
1765 */
1766 void wiki_write(const char *zIn, int flags){
1767 Blob in;
1768 blob_init(&in, zIn, -1);
1769 wiki_convert(&in, 0, flags);
1770 blob_reset(&in);
1771 }
1772
1773 /*
1774 ** COMMAND: test-wiki-render
1775 **
1776 ** Usage: %fossil test-wiki-render FILE [OPTIONS]
1777 **
@@ -1790,10 +1794,11 @@
1790 if( find_option("htmlonly",0,0)!=0 ) flags |= WIKI_HTMLONLY;
1791 if( find_option("linksonly",0,0)!=0 ) flags |= WIKI_LINKSONLY;
1792 if( find_option("nobadlinks",0,0)!=0 ) flags |= WIKI_NOBADLINKS;
1793 if( find_option("inline",0,0)!=0 ) flags |= WIKI_INLINE;
1794 if( find_option("noblock",0,0)!=0 ) flags |= WIKI_NOBLOCK;
 
1795 verify_all_options();
1796 if( g.argc!=3 ) usage("FILE");
1797 blob_zero(&out);
1798 blob_read_from_file(&in, g.argv[2], ExtFILE);
1799 wiki_convert(&in, &out, flags);
1800
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -30,10 +30,11 @@
30 #define WIKI_NOBLOCK 0x004 /* No block markup of any kind */
31 #define WIKI_BUTTONS 0x008 /* Allow sub-menu buttons */
32 #define WIKI_NOBADLINKS 0x010 /* Ignore broken hyperlinks */
33 #define WIKI_LINKSONLY 0x020 /* No markup. Only decorate links */
34 #define WIKI_NEWLINE 0x040 /* Honor \n - break lines at each \n */
35 #define WIKI_MARKDOWNLINKS 0x080 /* Resolve hyperlinks as in markdown */
36 #endif
37
38
39 /*
40 ** These are the only markup attributes allowed.
@@ -1211,20 +1212,22 @@
1212 ** [http://www.fossil-scm.org/]
1213 ** [https://www.fossil-scm.org/]
1214 ** [ftp://www.fossil-scm.org/]
1215 ** [mailto:[email protected]]
1216 **
1217 ** [/path] -> Refers to the root of the Fossil hierarchy, not
1218 ** the root of the URI domain
1219 **
1220 ** [./relpath]
1221 ** [../relpath]
1222 **
1223 ** [#fragment]
 
1224 **
1225 ** [0123456789abcdef]
1226 **
1227 ** [WikiPageName]
1228 ** [wiki:WikiPageName]
1229 **
1230 ** [2010-02-27 07:13]
1231 */
1232 void wiki_resolve_hyperlink(
1233 Blob *pOut, /* Write the HTML output here */
@@ -1296,24 +1299,35 @@
1299 blob_appendf(pOut, "%z[",xhref(zExtraNS, "%R/info/%s", zTarget));
1300 zTerm = "]</a>";
1301 }else{
1302 zTerm = "";
1303 }
 
 
 
1304 }else if( (z = validWikiPageName(mFlags, zTarget))!=0 ){
1305 /* The link is to a valid wiki page name */
1306 const char *zOverride = wiki_is_overridden(zTarget);
1307 if( zOverride ){
1308 blob_appendf(pOut, "<a href=\"%R/info/%S\"%s>", zOverride, zExtra);
1309 }else{
1310 blob_appendf(pOut, "<a href=\"%R/wiki?name=%T\"%s>", z, zExtra);
1311 }
1312 }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'
1313 && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){
1314 /* Dates or date-and-times in ISO8610 resolve to a link to the
1315 ** timeline for that date */
1316 blob_appendf(pOut, "<a href=\"%R/timeline?c=%T\"%s>", zTarget, zExtra);
1317 }else if( mFlags & WIKI_MARKDOWNLINKS ){
1318 /* If none of the above, and if rendering links for markdown, then
1319 ** create a link to the literal text of the target */
1320 blob_appendf(pOut, "<a href=\"%h\"%s>", zTarget, zExtra);
1321 }else if( zOrig && zTarget>=&zOrig[2]
1322 && zTarget[-1]=='[' && !fossil_isspace(zTarget[-2]) ){
1323 /* If the hyperlink markup is not preceded by whitespace, then it
1324 ** is probably a C-language subscript or similar, not really a
1325 ** hyperlink. Just ignore it. */
1326 zTerm = "";
1327 }else if( (mFlags & (WIKI_NOBADLINKS|WIKI_LINKSONLY))!=0 ){
1328 /* Also ignore the link if various flags are set */
1329 zTerm = "";
1330 }else{
1331 blob_appendf(pOut, "<span class=\"brokenlink\">[%h]", zTarget);
1332 zTerm = "</span>";
1333 }
@@ -1758,20 +1772,10 @@
1772 }
1773 blob_append(renderer.pOut, "\n", 1);
1774 free(renderer.aStack);
1775 }
1776
 
 
 
 
 
 
 
 
 
 
1777 /*
1778 ** COMMAND: test-wiki-render
1779 **
1780 ** Usage: %fossil test-wiki-render FILE [OPTIONS]
1781 **
@@ -1790,10 +1794,11 @@
1794 if( find_option("htmlonly",0,0)!=0 ) flags |= WIKI_HTMLONLY;
1795 if( find_option("linksonly",0,0)!=0 ) flags |= WIKI_LINKSONLY;
1796 if( find_option("nobadlinks",0,0)!=0 ) flags |= WIKI_NOBADLINKS;
1797 if( find_option("inline",0,0)!=0 ) flags |= WIKI_INLINE;
1798 if( find_option("noblock",0,0)!=0 ) flags |= WIKI_NOBLOCK;
1799 db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
1800 verify_all_options();
1801 if( g.argc!=3 ) usage("FILE");
1802 blob_zero(&out);
1803 blob_read_from_file(&in, g.argv[2], ExtFILE);
1804 wiki_convert(&in, &out, flags);
1805
+151 -69
--- www/defcsp.md
+++ www/defcsp.md
@@ -1,106 +1,188 @@
11
# The Default Content Security Policy (CSP)
22
3
-When Fossil’s web interface generates an HTML page, it
4
-normally includes a [Content Security Policy][csp] (CSP)
5
-in the `<head>`.
6
-The CSP tells the browser what types of content (HTML, image, CSS,
7
-JavaScript...) the document may reference and from where the
8
-content may be sourced.
9
-
10
-CSP is a security measure designed to prevent [cross-site scripting][xss]
11
-(XSS) and other similar code injection attacks.
12
-The CSP defines a “white list” of content types and origins that
13
-are considered safe. Any references to resources that are not
14
-on the white list are ignored.
15
-
16
-If Fossil were perfect and bug-free and never allowed any kind of
17
-code injection on the pages it generates, then the CSP would not
18
-be useful. The Fossil developers are not aware of any defects
19
-in Fossil that allow code injection, and will promptly fix any defects
20
-that are brought to their attention. Lots of eyeballs are looking at
21
-Fossil to find problems in the code, and the Fossil build process uses
22
-custom static analysis techniques to help identify code injection problems
23
-at compile-time. Nevertheless, problems do sometimes (rarely) slip
24
-through. The CSP serves as a final line of defense, preventing
25
-code injection defects in Fossil from turning into actual
26
-vulnerabilities.
3
+When Fossil’s web interface generates an HTML page, it normally includes
4
+a [Content Security Policy][csp] (CSP) in the `<head>`. The CSP defines
5
+a “white list” to tell the browser what types of content (HTML, images,
6
+CSS, JavaScript...) the document may reference and the sources the
7
+browser is allowed to pull such content from. The aim is to prevent
8
+certain classes of [cross-site scripting][xss] (XSS) and code injection
9
+attacks. The browser will not pull content types disallowed by the CSP.
10
+
11
+Fossil has built-in server-side content filtering logic. For example, it
12
+purposely breaks `<script>` tags when it finds them in Markdown and
13
+Fossil Wiki documents. (But not in [HTML-formatted embedded
14
+docs][hfed]!) We also back that with multiple levels of analysis and
15
+checks to find and fix content security problems: compile-time static
16
+analysis, run-time dynamic analysis, and manual code inspection. Fossil
17
+is open source software, so it benefits from the “[many
18
+eyeballs][llaw],” limited by the size of its developer community.
19
+
20
+However, there is a practical limit to the power of server-side
21
+filtering and code quality practices.
22
+
23
+First, there is an endless battle between those looking for clever paths
24
+around such barriers and those erecting the barriers. The developers of
25
+Fossil are committed to holding up our end of that fight, but this is,
26
+to some extent, a reactive posture. It is cold comfort if Fossil’s
27
+developers react quickly to a report of code injection — as we do! — if
28
+the bad guys learn of it and start exploiting it first.
29
+
30
+Second, Fossil has purposefully powerful features that are inherently
31
+difficult to police from the server side: HTML [in wiki](/wiki_rules)
32
+and [in Markdown](/md_rules) docs, [TH1 docs](./th1.md), etc.
33
+
34
+Fossil’s strong default CSP adds client-side filtering to backstop our
35
+server-side measures.
2736
2837
Fossil site administrators can [modify the default CSP](#override), perhaps
2938
to add trusted external sources for auxiliary content. But for maximum
3039
safety, site developers are encouraged to work within the restrictions
3140
imposed by the default CSP and avoid the temptation to relax the CSP
3241
unless they fully understand the security implications of what they are
3342
doing.
43
+
44
+[llaw]: https://en.wikipedia.org/wiki/Linus%27s_Law
45
+
3446
3547
## The Default Restrictions
3648
3749
The Fossil default CSP declares the following content restrictions:
3850
3951
4052
### <a name="base"></a> default-src 'self' data:
4153
42
-This policy means mixed-origin content isn’t allowed, so you can’t refer to
43
-resources (images, style-sheets, and scripts) on other web domains.
44
-Hence the following Markdown for an inline
45
-image hosted on another site will cause a CSP error:
54
+This policy means mixed-origin content isn’t allowed, so you can’t refer
55
+to resources on other web domains. Browsers will ignore a link like the
56
+one in the following Markdown under our default CSP:
4657
4758
![fancy 3D Fossil logotype](https://i.imgur.com/HalpMgt.png)
59
+
60
+If you look in the browser’s developer console, you should see a CSP
61
+error when attempting to render such a page.
4862
4963
The default policy does allows inline `data:` URIs, which means you could
5064
[data-encode][de] your image content and put it inline within the
5165
document:
5266
5367
![small inline image](data:image/gif;base64,R0lGODlh...)
5468
5569
That method is best used for fairly small resources. Large `data:` URIs
56
-are hard to read and edit. Keep in mind that if you put such a thing
57
-into a Fossil forum post, anyone subscribed to email alerts will get a
58
-copy of the raw URI text, which is really ugly.
70
+are hard to read and edit. There are secondary problems as well: if you
71
+put a large image into a Fossil forum post this way, anyone subscribed
72
+to email alerts will get a copy of the raw URI text, which can amount to
73
+pages and pages of [ugly Base64-encoded text][b64].
74
+
75
+Fossil offers several alternatives for serving large content resources
76
+from within the repository:
77
+
78
+* **versioned content** via [`/raw`](/help?cmd=/raw)
79
+* **[unversioned content](./unvers.wiki)** via [`/uv`](/help?cmd=/uv)
80
+* **relative links**
5981
60
-For larger images in [embedded documentation](./embeddeddoc.wiki) you
61
-store the image as a separate file in the Fossil repository and reference
62
-it via a relative URI.
82
+Only the first two options work in [wiki articles][wiki],
83
+[tickets][tkt], [forum posts][fp], and [tech notes][tn]. The last
84
+option is a much simpler alternative, but it only works within [embedded
85
+documentation][ed]:
6386
6487
![large inline image](./inlineimage.jpg)
6588
66
-Any content from the same domain as the Fossil repository will work fine.
67
-So if the Fossil repository is but one path in a larger website, it will
68
-be able reference other static resources within that same website. It
69
-can also reference [unversioned content](./unvers.wiki). However, as
70
-unversioned content and content outside of the Fossil repository itself
71
-will not be transferred by [sync](/help?cmd=sync), the references probably
72
-will not work in clones of your repository.
89
+Because all of these methods pull content from within the Fossil
90
+repository, they all count as “self” for the purposes of the CSP.
91
+
92
+This rule also works when the Fossil repository is but one path in a
93
+larger website. The browser can’t distinguish Fossil-served content from
94
+that served by the rest of the same web domain, so your repository can
95
+refer to other resources within that same web site, whether they are
96
+static files served by [an HTTP proxy in front of Fossil][svr], by
97
+another Fossil repository served under that same domain, or dynamic
98
+content served by, say, a PHP app on that same site.
99
+
100
+Beware that there are a number of problems that come up with using such
101
+out-of-repository resources, which all stem from the fact that they
102
+aren’t included in a [sync](/help?cmd=sync):
103
+
104
+1. Relative links break in `fossil ui` when run on a clone.
105
+
106
+2. Absolute links break under certain types of failover and
107
+ load-balancing schemes.
108
+
109
+3. Absolute links fail when one’s purpose in using a clone is to
110
+ recover from the loss of a project web site by standing that clone
111
+ up [as a server][svr] elsewhere.
112
+
113
+You can avoid all of these problems by referring to in-repo resources
114
+exclusively.
115
+
116
+[b64]: https://en.wikipedia.org/wiki/Base64
117
+[svr]: ./server/
118
+
73119
74120
### <a name="style"></a> style-src 'self' 'unsafe-inline'
75121
76
-This policy allows CSS information to come from separate files in the
77
-same domain of the Fossil server, or for CSS to be embedded inline within
78
-the document text.
79
-
80
-The `'unsafe-inline'` element means that an injection vulnerability in
81
-Fossil would allow an attacker to modify the CSS for a Fossil-generated
82
-page. This is not ideal, but nor is it as dangerous as allowing
83
-injected javascript to run, and Fossil uses of in-line CSS
84
-for things like setting background colors in timelines and defining
85
-line widths in bar graphs on the [Activity Reports](/reports) page,
86
-so it seems like in-line CSS is a necessary compromise at this time.
122
+This policy allows CSS information to come from separate files hosted
123
+under the Fossil repo server’s Internet domain, or for CSS to be
124
+embedded within `<style>` tags within the document text.
125
+
126
+The `'unsafe-inline'` declaration excludes CSS within individual HTML
127
+elements:
128
+
129
+ <p style="margin-left: 4em">Indented text.</p>
130
+
131
+Because this policy is weaker than [our default for script
132
+elements](#script), there is the potential for an atacker to modify a
133
+Fossil-generated page via CSS. While such page modifications are not as
134
+dangerous as injected JavaScript, the real reason we allow it is that
135
+Fossil still emits in-page `<style>` blocks in a few places. Over time,
136
+we may work out ways to avoid each of these, which will eventually allow
137
+us to tighten this CSP rule down to match the `script` rule. We
138
+recommend that you do your own CSS modifications [via the skin][cs]
139
+rather than depend on the ability to insert `<script>` blocks into
140
+individual pages.
141
+
87142
88143
### <a name="script"></a> script-src 'self' 'nonce-%s'
89144
90
-This policy disables in-line javascript and only allows `<script>`
91
-elements if the `<script>` includes a `nonce=` attribute the
92
-matches the %s section of the CSP. Fossil provides a different
93
-random nonce for every page it generates, and since an attacker has
94
-no way of predicting what that nonce will be, the attacker is unable
95
-to inject working javascript.
96
-
97
-For documents generated by the [CGI extensions](./serverext.wiki), the
98
-value of the nonce is accessible in the FOSSIL_NONCE environment variable.
99
-TH1 scripts that run while generating the header or footer can access
100
-the nonce in the $nonce variable. The JavaScript section of a
101
-[custom skin][cs] automatically includes the appropriate nonce.
145
+This policy disables in-line JavaScript and only allows `<script>`
146
+elements if the `<script>` includes a `nonce` attribute that matches the
147
+one declared by the CSP. That nonce is a large random number, unique for
148
+each HTTP page generated by Fossil, so an attacker cannot guess the
149
+value, so the browser will ignore an attacker’s injected JavaScript.
150
+
151
+That nonce can only come from one of three sources, all of which should
152
+be protected at the system administration level on the Fossil server:
153
+
154
+* **Fossil server C code:** All code paths in Fossil that emit
155
+ `<script>` elements include the `nonce` attribute. There are several
156
+ cases, such as the “JavaScript” section of a [custom skin][cs].
157
+ That text is currently inserted into each HTML page generated by
158
+ Fossil,¹ which means it needs to include a `nonce` attribute to
159
+ allow it to run under this default CSP. We consider JavaScript
160
+ emitted via these paths to be safe because it’s audited by the
161
+ Fossil developers. We assume that you got your Fossil server’s code
162
+ from a trustworthy source and that an attacker cannot replace your
163
+ Fossil server binary.
164
+
165
+* **TH1 code:** The Fossil TH1 interpreter pre-defines the [`$nonce`
166
+ variable](./th1.md#nonce) for use in [custom skins][cs]. For
167
+ example, some of the stock skins that ship with Fossil include a
168
+ wall clock feature up in the corner that updates once a minute.
169
+ These paths are safe in the default Fossil configuration because
170
+ only the [all-powerful Setup user][su] can write TH1 code that
171
+ executes in the server’s running context.
172
+
173
+ There is, however, [a default-disabled path](#xss) to beware of,
174
+ covered in the next section.
175
+
176
+* **[CGI server extensions][ext]:** Fossil exports the nonce to the
177
+ CGI in the `FOSSIL_NONCE` environment variable, which it can then
178
+ use in `<script>` elements it generates. Because these extensions
179
+ can only be installed by the Fossil server’s system administrator,
180
+ this path is also considered safe.
181
+
182
+[su]: ./admin-v-setup.md
183
+
102184
103185
#### <a name="xss"></a>Cross-Site Scripting via Ordinary User Capabilities
104186
105187
We’re so restrictive about how we treat JavaScript because it can lead
106188
to difficult-to-avoid scripting attacks. If we used the same CSP for
@@ -155,10 +237,11 @@
155237
since the only way to create or modify HTML-formatted embedded docs is
156238
through check-ins.
157239
158240
[ed]: ./embeddeddoc.wiki
159241
[edtf]: ./embeddeddoc.wiki#th1
242
+[ext]: ./serverext.wiki
160243
[fp]: ./forum.wiki
161244
[hfed]: ./embeddeddoc.wiki#html
162245
[tkt]: ./tickets.wiki
163246
[tn]: ./event.wiki
164247
[wiki]: ./wikitheory.wiki
@@ -220,23 +303,23 @@
220303
221304
This then tells you one way to override Fossil’s default CSP: provide
222305
your own HTML header in a custom skin.
223306
224307
A useful combination is to entirely override the default CSP in the skin
225
-but then provide a new CSP [in the front-end proxy layer](./server/)
308
+but then provide a new CSP [in the front-end proxy layer][svr]
226309
using any of the many reverse proxy servers that can define custom HTTP
227310
headers.
228311
229312
230313
------------
231314
232315
233316
**Asides and Digressions:**
234317
235
-1. There is actually a third context that can correctly insert this
236
- nonce attribute: [a CGI server extension](./serverext.wiki), by use of
237
- the `FOSSIL_NONCE` variable sent to the CGI by Fossil.
318
+1. Fossil might someday switch to serving the “JavaScript” section of a
319
+ custom skin as a virtual text file, allowing it to be cached by the
320
+ browser, reducing page load times.
238321
239322
2. The stock Bootstrap skin does actually include a `<head>` tag, but
240323
from Fossil 2.7 through Fossil 2.9, it just repeated the same CSP
241324
text that Fossil’s C code inserts into the HTML header for all other
242325
stock skins. With Fossil 2.10, the stock Bootstrap skin uses
@@ -245,6 +328,5 @@
245328
246329
[cs]: ./customskin.md
247330
[csp]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
248331
[de]: https://dopiaza.org/tools/datauri/index.php
249332
[xss]: https://en.wikipedia.org/wiki/Cross-site_scripting
250
-[xssci]: https://fossil-scm.org/forum/forumpost/e7c386b21f
251333
--- www/defcsp.md
+++ www/defcsp.md
@@ -1,106 +1,188 @@
1 # The Default Content Security Policy (CSP)
2
3 When Fossil’s web interface generates an HTML page, it
4 normally includes a [Content Security Policy][csp] (CSP)
5 in the `<head>`.
6 The CSP tells the browser what types of content (HTML, image, CSS,
7 JavaScript...) the document may reference and from where the
8 content may be sourced.
9
10 CSP is a security measure designed to prevent [cross-site scripting][xss]
11 (XSS) and other similar code injection attacks.
12 The CSP defines a “white list” of content types and origins that
13 are considered safe. Any references to resources that are not
14 on the white list are ignored.
15
16 If Fossil were perfect and bug-free and never allowed any kind of
17 code injection on the pages it generates, then the CSP would not
18 be useful. The Fossil developers are not aware of any defects
19 in Fossil that allow code injection, and will promptly fix any defects
20 that are brought to their attention. Lots of eyeballs are looking at
21 Fossil to find problems in the code, and the Fossil build process uses
22 custom static analysis techniques to help identify code injection problems
23 at compile-time. Nevertheless, problems do sometimes (rarely) slip
24 through. The CSP serves as a final line of defense, preventing
25 code injection defects in Fossil from turning into actual
26 vulnerabilities.
 
 
 
 
 
 
 
 
 
27
28 Fossil site administrators can [modify the default CSP](#override), perhaps
29 to add trusted external sources for auxiliary content. But for maximum
30 safety, site developers are encouraged to work within the restrictions
31 imposed by the default CSP and avoid the temptation to relax the CSP
32 unless they fully understand the security implications of what they are
33 doing.
 
 
 
34
35 ## The Default Restrictions
36
37 The Fossil default CSP declares the following content restrictions:
38
39
40 ### <a name="base"></a> default-src 'self' data:
41
42 This policy means mixed-origin content isn’t allowed, so you can’t refer to
43 resources (images, style-sheets, and scripts) on other web domains.
44 Hence the following Markdown for an inline
45 image hosted on another site will cause a CSP error:
46
47 ![fancy 3D Fossil logotype](https://i.imgur.com/HalpMgt.png)
 
 
 
48
49 The default policy does allows inline `data:` URIs, which means you could
50 [data-encode][de] your image content and put it inline within the
51 document:
52
53 ![small inline image](data:image/gif;base64,R0lGODlh...)
54
55 That method is best used for fairly small resources. Large `data:` URIs
56 are hard to read and edit. Keep in mind that if you put such a thing
57 into a Fossil forum post, anyone subscribed to email alerts will get a
58 copy of the raw URI text, which is really ugly.
 
 
 
 
 
 
 
 
59
60 For larger images in [embedded documentation](./embeddeddoc.wiki) you
61 store the image as a separate file in the Fossil repository and reference
62 it via a relative URI.
 
63
64 ![large inline image](./inlineimage.jpg)
65
66 Any content from the same domain as the Fossil repository will work fine.
67 So if the Fossil repository is but one path in a larger website, it will
68 be able reference other static resources within that same website. It
69 can also reference [unversioned content](./unvers.wiki). However, as
70 unversioned content and content outside of the Fossil repository itself
71 will not be transferred by [sync](/help?cmd=sync), the references probably
72 will not work in clones of your repository.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
74 ### <a name="style"></a> style-src 'self' 'unsafe-inline'
75
76 This policy allows CSS information to come from separate files in the
77 same domain of the Fossil server, or for CSS to be embedded inline within
78 the document text.
79
80 The `'unsafe-inline'` element means that an injection vulnerability in
81 Fossil would allow an attacker to modify the CSS for a Fossil-generated
82 page. This is not ideal, but nor is it as dangerous as allowing
83 injected javascript to run, and Fossil uses of in-line CSS
84 for things like setting background colors in timelines and defining
85 line widths in bar graphs on the [Activity Reports](/reports) page,
86 so it seems like in-line CSS is a necessary compromise at this time.
 
 
 
 
 
 
 
 
 
87
88 ### <a name="script"></a> script-src 'self' 'nonce-%s'
89
90 This policy disables in-line javascript and only allows `<script>`
91 elements if the `<script>` includes a `nonce=` attribute the
92 matches the %s section of the CSP. Fossil provides a different
93 random nonce for every page it generates, and since an attacker has
94 no way of predicting what that nonce will be, the attacker is unable
95 to inject working javascript.
96
97 For documents generated by the [CGI extensions](./serverext.wiki), the
98 value of the nonce is accessible in the FOSSIL_NONCE environment variable.
99 TH1 scripts that run while generating the header or footer can access
100 the nonce in the $nonce variable. The JavaScript section of a
101 [custom skin][cs] automatically includes the appropriate nonce.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
103 #### <a name="xss"></a>Cross-Site Scripting via Ordinary User Capabilities
104
105 We’re so restrictive about how we treat JavaScript because it can lead
106 to difficult-to-avoid scripting attacks. If we used the same CSP for
@@ -155,10 +237,11 @@
155 since the only way to create or modify HTML-formatted embedded docs is
156 through check-ins.
157
158 [ed]: ./embeddeddoc.wiki
159 [edtf]: ./embeddeddoc.wiki#th1
 
160 [fp]: ./forum.wiki
161 [hfed]: ./embeddeddoc.wiki#html
162 [tkt]: ./tickets.wiki
163 [tn]: ./event.wiki
164 [wiki]: ./wikitheory.wiki
@@ -220,23 +303,23 @@
220
221 This then tells you one way to override Fossil’s default CSP: provide
222 your own HTML header in a custom skin.
223
224 A useful combination is to entirely override the default CSP in the skin
225 but then provide a new CSP [in the front-end proxy layer](./server/)
226 using any of the many reverse proxy servers that can define custom HTTP
227 headers.
228
229
230 ------------
231
232
233 **Asides and Digressions:**
234
235 1. There is actually a third context that can correctly insert this
236 nonce attribute: [a CGI server extension](./serverext.wiki), by use of
237 the `FOSSIL_NONCE` variable sent to the CGI by Fossil.
238
239 2. The stock Bootstrap skin does actually include a `<head>` tag, but
240 from Fossil 2.7 through Fossil 2.9, it just repeated the same CSP
241 text that Fossil’s C code inserts into the HTML header for all other
242 stock skins. With Fossil 2.10, the stock Bootstrap skin uses
@@ -245,6 +328,5 @@
245
246 [cs]: ./customskin.md
247 [csp]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
248 [de]: https://dopiaza.org/tools/datauri/index.php
249 [xss]: https://en.wikipedia.org/wiki/Cross-site_scripting
250 [xssci]: https://fossil-scm.org/forum/forumpost/e7c386b21f
251
--- www/defcsp.md
+++ www/defcsp.md
@@ -1,106 +1,188 @@
1 # The Default Content Security Policy (CSP)
2
3 When Fossil’s web interface generates an HTML page, it normally includes
4 a [Content Security Policy][csp] (CSP) in the `<head>`. The CSP defines
5 a “white list” to tell the browser what types of content (HTML, images,
6 CSS, JavaScript...) the document may reference and the sources the
7 browser is allowed to pull such content from. The aim is to prevent
8 certain classes of [cross-site scripting][xss] (XSS) and code injection
9 attacks. The browser will not pull content types disallowed by the CSP.
10
11 Fossil has built-in server-side content filtering logic. For example, it
12 purposely breaks `<script>` tags when it finds them in Markdown and
13 Fossil Wiki documents. (But not in [HTML-formatted embedded
14 docs][hfed]!) We also back that with multiple levels of analysis and
15 checks to find and fix content security problems: compile-time static
16 analysis, run-time dynamic analysis, and manual code inspection. Fossil
17 is open source software, so it benefits from the “[many
18 eyeballs][llaw],” limited by the size of its developer community.
19
20 However, there is a practical limit to the power of server-side
21 filtering and code quality practices.
22
23 First, there is an endless battle between those looking for clever paths
24 around such barriers and those erecting the barriers. The developers of
25 Fossil are committed to holding up our end of that fight, but this is,
26 to some extent, a reactive posture. It is cold comfort if Fossil’s
27 developers react quickly to a report of code injection — as we do! — if
28 the bad guys learn of it and start exploiting it first.
29
30 Second, Fossil has purposefully powerful features that are inherently
31 difficult to police from the server side: HTML [in wiki](/wiki_rules)
32 and [in Markdown](/md_rules) docs, [TH1 docs](./th1.md), etc.
33
34 Fossil’s strong default CSP adds client-side filtering to backstop our
35 server-side measures.
36
37 Fossil site administrators can [modify the default CSP](#override), perhaps
38 to add trusted external sources for auxiliary content. But for maximum
39 safety, site developers are encouraged to work within the restrictions
40 imposed by the default CSP and avoid the temptation to relax the CSP
41 unless they fully understand the security implications of what they are
42 doing.
43
44 [llaw]: https://en.wikipedia.org/wiki/Linus%27s_Law
45
46
47 ## The Default Restrictions
48
49 The Fossil default CSP declares the following content restrictions:
50
51
52 ### <a name="base"></a> default-src 'self' data:
53
54 This policy means mixed-origin content isn’t allowed, so you can’t refer
55 to resources on other web domains. Browsers will ignore a link like the
56 one in the following Markdown under our default CSP:
 
57
58 ![fancy 3D Fossil logotype](https://i.imgur.com/HalpMgt.png)
59
60 If you look in the browser’s developer console, you should see a CSP
61 error when attempting to render such a page.
62
63 The default policy does allows inline `data:` URIs, which means you could
64 [data-encode][de] your image content and put it inline within the
65 document:
66
67 ![small inline image](data:image/gif;base64,R0lGODlh...)
68
69 That method is best used for fairly small resources. Large `data:` URIs
70 are hard to read and edit. There are secondary problems as well: if you
71 put a large image into a Fossil forum post this way, anyone subscribed
72 to email alerts will get a copy of the raw URI text, which can amount to
73 pages and pages of [ugly Base64-encoded text][b64].
74
75 Fossil offers several alternatives for serving large content resources
76 from within the repository:
77
78 * **versioned content** via [`/raw`](/help?cmd=/raw)
79 * **[unversioned content](./unvers.wiki)** via [`/uv`](/help?cmd=/uv)
80 * **relative links**
81
82 Only the first two options work in [wiki articles][wiki],
83 [tickets][tkt], [forum posts][fp], and [tech notes][tn]. The last
84 option is a much simpler alternative, but it only works within [embedded
85 documentation][ed]:
86
87 ![large inline image](./inlineimage.jpg)
88
89 Because all of these methods pull content from within the Fossil
90 repository, they all count as “self” for the purposes of the CSP.
91
92 This rule also works when the Fossil repository is but one path in a
93 larger website. The browser can’t distinguish Fossil-served content from
94 that served by the rest of the same web domain, so your repository can
95 refer to other resources within that same web site, whether they are
96 static files served by [an HTTP proxy in front of Fossil][svr], by
97 another Fossil repository served under that same domain, or dynamic
98 content served by, say, a PHP app on that same site.
99
100 Beware that there are a number of problems that come up with using such
101 out-of-repository resources, which all stem from the fact that they
102 aren’t included in a [sync](/help?cmd=sync):
103
104 1. Relative links break in `fossil ui` when run on a clone.
105
106 2. Absolute links break under certain types of failover and
107 load-balancing schemes.
108
109 3. Absolute links fail when one’s purpose in using a clone is to
110 recover from the loss of a project web site by standing that clone
111 up [as a server][svr] elsewhere.
112
113 You can avoid all of these problems by referring to in-repo resources
114 exclusively.
115
116 [b64]: https://en.wikipedia.org/wiki/Base64
117 [svr]: ./server/
118
119
120 ### <a name="style"></a> style-src 'self' 'unsafe-inline'
121
122 This policy allows CSS information to come from separate files hosted
123 under the Fossil repo server’s Internet domain, or for CSS to be
124 embedded within `<style>` tags within the document text.
125
126 The `'unsafe-inline'` declaration excludes CSS within individual HTML
127 elements:
128
129 <p style="margin-left: 4em">Indented text.</p>
130
131 Because this policy is weaker than [our default for script
132 elements](#script), there is the potential for an atacker to modify a
133 Fossil-generated page via CSS. While such page modifications are not as
134 dangerous as injected JavaScript, the real reason we allow it is that
135 Fossil still emits in-page `<style>` blocks in a few places. Over time,
136 we may work out ways to avoid each of these, which will eventually allow
137 us to tighten this CSP rule down to match the `script` rule. We
138 recommend that you do your own CSS modifications [via the skin][cs]
139 rather than depend on the ability to insert `<script>` blocks into
140 individual pages.
141
142
143 ### <a name="script"></a> script-src 'self' 'nonce-%s'
144
145 This policy disables in-line JavaScript and only allows `<script>`
146 elements if the `<script>` includes a `nonce` attribute that matches the
147 one declared by the CSP. That nonce is a large random number, unique for
148 each HTTP page generated by Fossil, so an attacker cannot guess the
149 value, so the browser will ignore an attacker’s injected JavaScript.
150
151 That nonce can only come from one of three sources, all of which should
152 be protected at the system administration level on the Fossil server:
153
154 * **Fossil server C code:** All code paths in Fossil that emit
155 `<script>` elements include the `nonce` attribute. There are several
156 cases, such as the “JavaScript” section of a [custom skin][cs].
157 That text is currently inserted into each HTML page generated by
158 Fossil,¹ which means it needs to include a `nonce` attribute to
159 allow it to run under this default CSP. We consider JavaScript
160 emitted via these paths to be safe because it’s audited by the
161 Fossil developers. We assume that you got your Fossil server’s code
162 from a trustworthy source and that an attacker cannot replace your
163 Fossil server binary.
164
165 * **TH1 code:** The Fossil TH1 interpreter pre-defines the [`$nonce`
166 variable](./th1.md#nonce) for use in [custom skins][cs]. For
167 example, some of the stock skins that ship with Fossil include a
168 wall clock feature up in the corner that updates once a minute.
169 These paths are safe in the default Fossil configuration because
170 only the [all-powerful Setup user][su] can write TH1 code that
171 executes in the server’s running context.
172
173 There is, however, [a default-disabled path](#xss) to beware of,
174 covered in the next section.
175
176 * **[CGI server extensions][ext]:** Fossil exports the nonce to the
177 CGI in the `FOSSIL_NONCE` environment variable, which it can then
178 use in `<script>` elements it generates. Because these extensions
179 can only be installed by the Fossil server’s system administrator,
180 this path is also considered safe.
181
182 [su]: ./admin-v-setup.md
183
184
185 #### <a name="xss"></a>Cross-Site Scripting via Ordinary User Capabilities
186
187 We’re so restrictive about how we treat JavaScript because it can lead
188 to difficult-to-avoid scripting attacks. If we used the same CSP for
@@ -155,10 +237,11 @@
237 since the only way to create or modify HTML-formatted embedded docs is
238 through check-ins.
239
240 [ed]: ./embeddeddoc.wiki
241 [edtf]: ./embeddeddoc.wiki#th1
242 [ext]: ./serverext.wiki
243 [fp]: ./forum.wiki
244 [hfed]: ./embeddeddoc.wiki#html
245 [tkt]: ./tickets.wiki
246 [tn]: ./event.wiki
247 [wiki]: ./wikitheory.wiki
@@ -220,23 +303,23 @@
303
304 This then tells you one way to override Fossil’s default CSP: provide
305 your own HTML header in a custom skin.
306
307 A useful combination is to entirely override the default CSP in the skin
308 but then provide a new CSP [in the front-end proxy layer][svr]
309 using any of the many reverse proxy servers that can define custom HTTP
310 headers.
311
312
313 ------------
314
315
316 **Asides and Digressions:**
317
318 1. Fossil might someday switch to serving the “JavaScript” section of a
319 custom skin as a virtual text file, allowing it to be cached by the
320 browser, reducing page load times.
321
322 2. The stock Bootstrap skin does actually include a `<head>` tag, but
323 from Fossil 2.7 through Fossil 2.9, it just repeated the same CSP
324 text that Fossil’s C code inserts into the HTML header for all other
325 stock skins. With Fossil 2.10, the stock Bootstrap skin uses
@@ -245,6 +328,5 @@
328
329 [cs]: ./customskin.md
330 [csp]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
331 [de]: https://dopiaza.org/tools/datauri/index.php
332 [xss]: https://en.wikipedia.org/wiki/Cross-site_scripting
 
333
--- www/server/any/althttpd.md
+++ www/server/any/althttpd.md
@@ -1,37 +1,42 @@
11
# Serving via althttpd
22
3
-The public SQLite and Fossil web sites are not purely served by Fossil
4
-for two reasons:
5
-
6
-1. We want access to these sites to be secured with TLS, which we do
7
- [via `stunnel`](./stunnel.md).
8
-
9
-2. Parts of these web sites are static, stored as plain files on disk,
10
- not as Fossil artifacts. We serve such files using a separate web
11
- server called [`althttpd`][ah], written by the primary author of
12
- both SQLite and Fossil, D. Richard Hipp. `althttpd` is a lightweight
13
- HTTP-only web server. It handles the static HTTP hits on
14
- <tt>sqlite.org</tt> and <tt>fossil-scm.org</tt>, delegating HTTPS
15
- hits to `stunnel` and dynamic content hits to Fossil [via
16
- CGI][cgi].
17
-
18
-The largest single chunk of static content served directly by `althttpd`
19
-rather than via Fossil is the [SQLite documentation][sd], which is built
20
-[from source files][ds]. We don’t want those output files stored in
21
-Fossil; we already keep that process’s *input* files in Fossil. Thus the
22
-choice to serve the output statically.
23
-
24
-In addition to the [server’s documentation page][ah], there is a large,
25
-helpful header comment in the server’s [single-file C
26
-implementation][ac]. Between that and the generic [Serving via CGI][cgi]
27
-docs, you should be able to figure out how to serve Fossil via
28
-`althttpd`.
3
+[Althttpd][althttpd]
4
+is a light-weight web server that has been used to implement the SQLite and
5
+Fossil websites for well over a decade. Althttpd strives for simplicity,
6
+security, ease of configuration, and low resource usage.
7
+
8
+To set up a Fossil server as CGI on a host running the althttpd web
9
+server, follow these steps.
10
+<ol>
11
+<li<p>Get the althttpd webserver running on the host. This is easily
12
+done by following the [althttpd documentation][althttpd].
13
+
14
+<li><p>Create a CGI script for your Fossil respository. The script will
15
+be typically be two lines of code that look something like this:
16
+
17
+~~~
18
+ #!/usr/bin/fossil
19
+ repository: /home/yourlogin/fossils/project.fossil
20
+~~~
21
+
22
+Modify the filenames to conform to your system, of course. The
23
+CGI script accepts [other options][cgi] besides the
24
+repository:" line. You can add in other options as you desire,
25
+but the single "repository:" line is normally all that is needed
26
+to get started.
27
+
28
+<li><p>Make the CGI script executable.
29
+
30
+<li><p>Verify that the fossil repository file and the directory that contains
31
+the repository are both writable by whatever user the web server is
32
+running and.
33
+</ol>
34
+
35
+And you are done. Visit the URL that corresponds to the CGI script
36
+you created to start using your Fossil server.
2937
3038
*[Return to the top-level Fossil server article.](../)*
3139
3240
33
-[ac]: https://sqlite.org/docsrc/file/misc/althttpd.c
34
-[ah]: https://sqlite.org/docsrc/doc/trunk/misc/althttpd.md
35
-[cgi]: ./cgi.md
36
-[ds]: https://sqlite.org/docsrc/
37
-[sd]: https://sqlite.org/docs.html
41
+[althttpd]: https://sqlite.org/docsrc/doc/trunk/misc/althttpd.md
42
+[cgi]: ../../cgi.wiki
3843
--- www/server/any/althttpd.md
+++ www/server/any/althttpd.md
@@ -1,37 +1,42 @@
1 # Serving via althttpd
2
3 The public SQLite and Fossil web sites are not purely served by Fossil
4 for two reasons:
5
6 1. We want access to these sites to be secured with TLS, which we do
7 [via `stunnel`](./stunnel.md).
8
9 2. Parts of these web sites are static, stored as plain files on disk,
10 not as Fossil artifacts. We serve such files using a separate web
11 server called [`althttpd`][ah], written by the primary author of
12 both SQLite and Fossil, D. Richard Hipp. `althttpd` is a lightweight
13 HTTP-only web server. It handles the static HTTP hits on
14 <tt>sqlite.org</tt> and <tt>fossil-scm.org</tt>, delegating HTTPS
15 hits to `stunnel` and dynamic content hits to Fossil [via
16 CGI][cgi].
17
18 The largest single chunk of static content served directly by `althttpd`
19 rather than via Fossil is the [SQLite documentation][sd], which is built
20 [from source files][ds]. We don’t want those output files stored in
21 Fossil; we already keep that process’s *input* files in Fossil. Thus the
22 choice to serve the output statically.
23
24 In addition to the [server’s documentation page][ah], there is a large,
25 helpful header comment in the server’s [single-file C
26 implementation][ac]. Between that and the generic [Serving via CGI][cgi]
27 docs, you should be able to figure out how to serve Fossil via
28 `althttpd`.
 
 
 
 
 
 
 
 
29
30 *[Return to the top-level Fossil server article.](../)*
31
32
33 [ac]: https://sqlite.org/docsrc/file/misc/althttpd.c
34 [ah]: https://sqlite.org/docsrc/doc/trunk/misc/althttpd.md
35 [cgi]: ./cgi.md
36 [ds]: https://sqlite.org/docsrc/
37 [sd]: https://sqlite.org/docs.html
38
--- www/server/any/althttpd.md
+++ www/server/any/althttpd.md
@@ -1,37 +1,42 @@
1 # Serving via althttpd
2
3 [Althttpd][althttpd]
4 is a light-weight web server that has been used to implement the SQLite and
5 Fossil websites for well over a decade. Althttpd strives for simplicity,
6 security, ease of configuration, and low resource usage.
7
8 To set up a Fossil server as CGI on a host running the althttpd web
9 server, follow these steps.
10 <ol>
11 <li<p>Get the althttpd webserver running on the host. This is easily
12 done by following the [althttpd documentation][althttpd].
13
14 <li><p>Create a CGI script for your Fossil respository. The script will
15 be typically be two lines of code that look something like this:
16
17 ~~~
18 #!/usr/bin/fossil
19 repository: /home/yourlogin/fossils/project.fossil
20 ~~~
21
22 Modify the filenames to conform to your system, of course. The
23 CGI script accepts [other options][cgi] besides the
24 repository:" line. You can add in other options as you desire,
25 but the single "repository:" line is normally all that is needed
26 to get started.
27
28 <li><p>Make the CGI script executable.
29
30 <li><p>Verify that the fossil repository file and the directory that contains
31 the repository are both writable by whatever user the web server is
32 running and.
33 </ol>
34
35 And you are done. Visit the URL that corresponds to the CGI script
36 you created to start using your Fossil server.
37
38 *[Return to the top-level Fossil server article.](../)*
39
40
41 [althttpd]: https://sqlite.org/docsrc/doc/trunk/misc/althttpd.md
42 [cgi]: ../../cgi.wiki
 
 
 
43
--- www/server/any/scgi.md
+++ www/server/any/scgi.md
@@ -9,20 +9,30 @@
99
This can be used with a web server such as [nginx](http://nginx.org)
1010
which does not support [Fossil’s CGI mode](./cgi.md).
1111
1212
A basic nginx configuration to support SCGI with Fossil looks like this:
1313
14
- location /example/ {
14
+ location /code/ {
1515
include scgi_params;
16
+ scgi_param SCRIPT_NAME "/code";
1617
scgi_pass localhost:9000;
17
- scgi_param SCRIPT_NAME "/example";
18
- scgi_param HTTPS "on";
1918
}
2019
21
-Start Fossil so that it will respond to nginx’s SCGI calls like this:
20
+The `scgi_params` file comes with nginx, and it simply translates nginx
21
+internal variables to `scgi_param` directives to create SCGI environment
22
+variables for the proxied program; in this case, Fossil. Our explicit
23
+`scgi_param` call to define `SCRIPT_NAME` adds one more variable to this
24
+set, which is necessary for this configuration to work properly, because
25
+our repo isn’t at the root of the URL hierarchy. Without it, when Fossil
26
+generates absolute URLs, they’ll be missing the `/code` part at the
27
+start, which will typically cause [404 errors][404].
28
+
29
+The final directive simply tells nginx to proxy all calls to URLs under
30
+`/code` down to an SCGI program on TCP port 9000. We can temporarily
31
+set Fossil up as a server on that port like so:
2232
23
- fossil server /path/to/repo.fossil --scgi --localhost --port 9000
33
+ $ fossil server /path/to/repo.fossil --scgi --localhost --port 9000 &
2434
2535
The `--scgi` option switches Fossil into SCGI mode from its default,
2636
which is [stand-alone HTTP server mode](./none.md). All of the other
2737
options discussed in that linked document — such as the ability to serve
2838
a directory full of Fossil repositories rather than just a single
@@ -34,23 +44,27 @@
3444
3545
Giving an explicit non-default TCP port number via `--port` is a good
3646
idea to avoid conflicts with use of Fossil’s default TCP service port,
3747
8080, which may conflict with local uses of `fossil ui` and such.
3848
39
-Fossil requires the `SCRIPT_NAME` environment variable in order to
40
-function properly, but nginx does not provide this variable by default,
41
-so it is necessary to provide it in the configuration. Failure to do
42
-this will cause Fossil to return an error.
43
-
44
-The [example `fslsrv` script](/file/tools/fslsrv) shows off these same
45
-concepts in a more complicated setting. You might want to mine that
46
-script for ideas.
47
-
48
-You might want to next read one of the platform-specific versions of this
49
-document, which goes into more detail:
50
-
51
-* [Debian/Ubuntu](../debian/nginx.md)
52
-
53
-There is a [separate article](../../tls-nginx.md) showing how to add TLS
54
-encryption to this basic SCGI + nginx setup.
49
+We characterized the SCGI service start command above as “temporary”
50
+because running Fossil in the background like that means it won’t start
51
+back up on a reboot of the server. A simple solution to that is to add
52
+that command to `/etc/rc.local` on systems that have it. However, you
53
+might want to consider setting Fossil up as an OS service instead, so
54
+that you get the benefits of the platform’s service management
55
+framework:
56
+
57
+* [Linux (systemd)](../debian/service.md)
58
+* [Windows service](../windows/service.md)
59
+* [macOS (launchd)](../macos/service.md)
60
+* [xinetd](../any/xinetd.md)
61
+* [inetd](../any/inetd.md)
62
+
63
+We go into more detail on nginx service setup with Fossil in our
64
+[Debian/Ubuntu specific guide](../debian/nginx.md). Then in [a later
65
+article](../../tls-nginx.md) that builds upon that, we show how to add
66
+TLS encryption to this basic SCGI + nginx setup on Debian type OSes.
5567
5668
*[Return to the top-level Fossil server article.](../)*
69
+
70
+[404]: https://en.wikipedia.org/wiki/HTTP_404
5771
--- www/server/any/scgi.md
+++ www/server/any/scgi.md
@@ -9,20 +9,30 @@
9 This can be used with a web server such as [nginx](http://nginx.org)
10 which does not support [Fossil’s CGI mode](./cgi.md).
11
12 A basic nginx configuration to support SCGI with Fossil looks like this:
13
14 location /example/ {
15 include scgi_params;
 
16 scgi_pass localhost:9000;
17 scgi_param SCRIPT_NAME "/example";
18 scgi_param HTTPS "on";
19 }
20
21 Start Fossil so that it will respond to nginx’s SCGI calls like this:
 
 
 
 
 
 
 
 
 
 
 
22
23 fossil server /path/to/repo.fossil --scgi --localhost --port 9000
24
25 The `--scgi` option switches Fossil into SCGI mode from its default,
26 which is [stand-alone HTTP server mode](./none.md). All of the other
27 options discussed in that linked document — such as the ability to serve
28 a directory full of Fossil repositories rather than just a single
@@ -34,23 +44,27 @@
34
35 Giving an explicit non-default TCP port number via `--port` is a good
36 idea to avoid conflicts with use of Fossil’s default TCP service port,
37 8080, which may conflict with local uses of `fossil ui` and such.
38
39 Fossil requires the `SCRIPT_NAME` environment variable in order to
40 function properly, but nginx does not provide this variable by default,
41 so it is necessary to provide it in the configuration. Failure to do
42 this will cause Fossil to return an error.
43
44 The [example `fslsrv` script](/file/tools/fslsrv) shows off these same
45 concepts in a more complicated setting. You might want to mine that
46 script for ideas.
47
48 You might want to next read one of the platform-specific versions of this
49 document, which goes into more detail:
50
51 * [Debian/Ubuntu](../debian/nginx.md)
52
53 There is a [separate article](../../tls-nginx.md) showing how to add TLS
54 encryption to this basic SCGI + nginx setup.
 
 
55
56 *[Return to the top-level Fossil server article.](../)*
 
 
57
--- www/server/any/scgi.md
+++ www/server/any/scgi.md
@@ -9,20 +9,30 @@
9 This can be used with a web server such as [nginx](http://nginx.org)
10 which does not support [Fossil’s CGI mode](./cgi.md).
11
12 A basic nginx configuration to support SCGI with Fossil looks like this:
13
14 location /code/ {
15 include scgi_params;
16 scgi_param SCRIPT_NAME "/code";
17 scgi_pass localhost:9000;
 
 
18 }
19
20 The `scgi_params` file comes with nginx, and it simply translates nginx
21 internal variables to `scgi_param` directives to create SCGI environment
22 variables for the proxied program; in this case, Fossil. Our explicit
23 `scgi_param` call to define `SCRIPT_NAME` adds one more variable to this
24 set, which is necessary for this configuration to work properly, because
25 our repo isn’t at the root of the URL hierarchy. Without it, when Fossil
26 generates absolute URLs, they’ll be missing the `/code` part at the
27 start, which will typically cause [404 errors][404].
28
29 The final directive simply tells nginx to proxy all calls to URLs under
30 `/code` down to an SCGI program on TCP port 9000. We can temporarily
31 set Fossil up as a server on that port like so:
32
33 $ fossil server /path/to/repo.fossil --scgi --localhost --port 9000 &
34
35 The `--scgi` option switches Fossil into SCGI mode from its default,
36 which is [stand-alone HTTP server mode](./none.md). All of the other
37 options discussed in that linked document — such as the ability to serve
38 a directory full of Fossil repositories rather than just a single
@@ -34,23 +44,27 @@
44
45 Giving an explicit non-default TCP port number via `--port` is a good
46 idea to avoid conflicts with use of Fossil’s default TCP service port,
47 8080, which may conflict with local uses of `fossil ui` and such.
48
49 We characterized the SCGI service start command above as “temporary”
50 because running Fossil in the background like that means it won’t start
51 back up on a reboot of the server. A simple solution to that is to add
52 that command to `/etc/rc.local` on systems that have it. However, you
53 might want to consider setting Fossil up as an OS service instead, so
54 that you get the benefits of the platform’s service management
55 framework:
56
57 * [Linux (systemd)](../debian/service.md)
58 * [Windows service](../windows/service.md)
59 * [macOS (launchd)](../macos/service.md)
60 * [xinetd](../any/xinetd.md)
61 * [inetd](../any/inetd.md)
62
63 We go into more detail on nginx service setup with Fossil in our
64 [Debian/Ubuntu specific guide](../debian/nginx.md). Then in [a later
65 article](../../tls-nginx.md) that builds upon that, we show how to add
66 TLS encryption to this basic SCGI + nginx setup on Debian type OSes.
67
68 *[Return to the top-level Fossil server article.](../)*
69
70 [404]: https://en.wikipedia.org/wiki/HTTP_404
71
--- www/server/debian/nginx.md
+++ www/server/debian/nginx.md
@@ -66,15 +66,15 @@
6666
6767
Fossil provides four major ways to access a repository it’s serving
6868
remotely, three of which are straightforward to use with nginx:
6969
7070
* **HTTP** — Fossil has a built-in HTTP server: [`fossil
71
- server`](/help/server). While this method is efficient and it’s
72
- possible to use nginx to proxy access to another HTTP server, this
73
- option is overkill for our purposes. nginx is itself a fully
74
- featured HTTP server, so we will choose in this guide not to make
75
- nginx reinterpret Fossil’s implementation of HTTP.
71
+ server`](../any/none.md). While this method is efficient and it’s
72
+ possible to use nginx to proxy access to another HTTP server, we
73
+ don’t see any particularly good reason to make nginx reinterpret
74
+ Fossil’s own implementation of HTTP when we have a better option.
75
+ (But see [below](#http).)
7676
7777
* **CGI** — This method is simple but inefficient, because it launches
7878
a separate Fossil instance on every HTTP hit.
7979
8080
Since Fossil is a relatively small self-contained program, and it’s
@@ -107,103 +107,74 @@
107107
$ sudo apt install fossil nginx
108108
109109
110110
## <a name="scgi"></a>Running Fossil in SCGI Mode
111111
112
-I run my Fossil SCGI server instances with a variant of [the `fslsrv`
113
-shell script](/file/tools/fslsrv) currently hosted in the Fossil source
114
-code repository. You’ll want to download that and make a copy of it, so
115
-you can customize it to your particular needs.
116
-
117
-This script allows running multiple Fossil SCGI servers, one per
118
-repository, each bound to a different high-numbered `localhost` port, so
119
-that only nginx can see and proxy them out to the public. The
120
-“`example`” repo is on TCP port localhost:12345, and the “`foo`” repo is
121
-on localhost:12346.
122
-
123
-As written, the `fslsrv` script expects repositories to be stored in the
124
-calling user’s home directory under `~/museum`, because where else do
125
-you keep Fossils?
126
-
127
-That home directory also needs to have a directory to hold log files,
128
-`~/log/fossil/*.log`. Fossil doesn’t put out much logging, but when it
129
-does, it’s better to have it captured than to need to re-create the
130
-problem after the fact.
131
-
132
-The use of `--baseurl` in this script lets us have each Fossil
133
-repository mounted in a different location in the URL scheme. Here, for
134
-example, we’re saying that the “`example`” repository is hosted under
135
-the `/code` URI on its domains, but that the “`foo`” repo is hosted at
136
-the top level of its domain. You’ll want to do something like the
137
-former for a Fossil repo that’s just one piece of a larger site, but the
138
-latter for a repo that is basically the whole point of the site.
139
-
140
-You might also want another script to automate the update, build, and
141
-deployment steps for new Fossil versions:
142
-
143
- #!/bin/sh
144
- cd $HOME/src/fossil/trunk
145
- fossil up
146
- make -j11
147
- killall fossil
148
- sudo make install
149
- fslsrv
150
-
151
-The `killall fossil` step is needed only on OSes that refuse to let you
152
-replace a running binary on disk.
153
-
154
-As written, the `fslsrv` script assumes a Linux environment. It expects
155
-`/bin/bash` to exist, and it depends on non-POSIX tools like `pgrep`.
156
-It should not be difficult to port to systems like macOS or the BSDs.
112
+For the following nginx configuration to work, it needs to contact a
113
+Fossil instance speaking the SCGI protocol. There are [many ways](../)
114
+to set that up. For Debian type systems, we primarily recommend
115
+following [our systemd user service guide](service.md).
116
+
117
+Another option would be to customize [the `fslsrv` shell
118
+script](/file/tools/fslsrv) that ships with Fossil as an example of
119
+launching multiple Fossil instances in the background to serve multiple
120
+URLs.
121
+
122
+However you do it, you need to match up the TCP port numbers between it
123
+and those in the nginx configuration below.
157124
158125
159126
## <a name="config"></a>Configuration
160127
161128
On Debian and Ubuntu systems the primary user-level configuration file
162129
for nginx is `/etc/nginx/sites-enabled/default`. I recommend that this
163130
file contain only a list of include statements, one for each site that
164131
server hosts:
165132
166
- include local/example
167
- include local/foo
133
+ include local/example.com
134
+ include local/foo.net
168135
169136
Those files then each define one domain’s configuration. Here,
170
-`/etc/nginx/local/example` contains the configuration for
171
-`*.example.com` and `*.example.net`; and `local/foo` contains the
172
-configuration for `*.foo.net`.
137
+`/etc/nginx/local/example.com` contains the configuration for
138
+`*.example.com` and its alias `*.example.net`; and `local/foo.net`
139
+contains the configuration for `*.foo.net`.
173140
174
-The configuration for our `foo.net` web site, stored in
175
-`/etc/nginx/sites-enabled/local/foo` is:
141
+The configuration for our `example.com` web site, stored in
142
+`/etc/nginx/sites-enabled/local/example.com` is:
176143
177144
server {
178
- server_name .foo.net;
145
+ server_name .example.com .example.net "";
179146
include local/generic;
180147
181
- access_log /var/log/nginx/foo.net-https-access.log;
182
- error_log /var/log/nginx/foo.net-https-error.log;
148
+ access_log /var/log/nginx/example.com-https-access.log;
149
+ error_log /var/log/nginx/example.com-https-error.log;
183150
184
- # Bypass Fossil for the static Doxygen docs
185
- location /doc/html {
186
- root /var/www/foo.net;
151
+ # Bypass Fossil for the static documentation generated from
152
+ # our source code by Doxygen, so it merges into the embedded
153
+ # doc URL hierarchy at Fossil’s $ROOT/doc without requiring that
154
+ # these generated files actually be stored in the repo. This
155
+ # also lets us set aggressive caching on these docs, since
156
+ # they rarely change.
157
+ location /code/doc/html {
158
+ root /var/www/example.com/code/doc/html;
187159
188160
location ~* \.(html|ico|css|js|gif|jpg|png)$ {
189161
expires 7d;
190162
add_header Vary Accept-Encoding;
191163
access_log off;
192164
}
193165
}
194166
195167
# Redirect everything else to the Fossil instance
196
- location / {
168
+ location /code {
197169
include scgi_params;
170
+ scgi_param SCRIPT_NAME "/code";
198171
scgi_pass 127.0.0.1:12345;
199
- scgi_param HTTPS "on";
200
- scgi_param SCRIPT_NAME "";
201172
}
202173
}
203174
204
-As you can see, this is a simple extension of [the basic nginx service
175
+As you can see, this is a pure extension of [the basic nginx service
205176
configuration for SCGI][scgii], showing off a few ideas you might want to
206177
try on your own site, such as static asset proxying.
207178
208179
The `local/generic` file referenced above helps us reduce unnecessary
209180
repetition among the multiple sites this configuration hosts:
@@ -221,10 +192,34 @@
221192
the `access_log` and `error_log` directives, which follow an obvious
222193
pattern from one host to the next. Sadly, you must tolerate some
223194
repetition across `server { }` blocks when setting up multiple domains
224195
on a single server.
225196
226
-The configuration for `example.com` and `example.net` is similar.
197
+The configuration for `foo.net` is similar.
227198
228199
See [the nginx docs](http://nginx.org/en/docs/) for more ideas.
229200
201
+
202
+## <a name="http"></a>Proxying HTTP Anyway
203
+
204
+[Above](#modes), we argued that proxying SCGI is a better option than
205
+making nginx reinterpret Fossil’s own implementation of HTTP. If you
206
+want Fossil to speak HTTP, just [set Fossil up as a standalone
207
+server](../any/none.md). And if you want nginx to [provide TLS
208
+encryption for Fossil][tls], proxying HTTP instead of SCGI provides no
209
+benefit.
210
+
211
+However, it is still worth showing the proper method of proxying
212
+Fossil’s HTTP server through nginx if only to make reading nginx
213
+documentation on other sites easier:
214
+
215
+ location /code {
216
+ rewrite ^/code(/.*) $1 break;
217
+ proxy_pass http://127.0.0.1:12345;
218
+ }
219
+
220
+The most common thing people get wrong when hand-rolling a configuration
221
+like this is to get the slashes wrong. Fossil is senstitive to this. For
222
+instance, Fossil will not collapse double slashes down to a single
223
+slash, as some other HTTP servers will.
224
+
230225
*[Return to the top-level Fossil server article.](../)*
231226
--- www/server/debian/nginx.md
+++ www/server/debian/nginx.md
@@ -66,15 +66,15 @@
66
67 Fossil provides four major ways to access a repository it’s serving
68 remotely, three of which are straightforward to use with nginx:
69
70 * **HTTP** — Fossil has a built-in HTTP server: [`fossil
71 server`](/help/server). While this method is efficient and it’s
72 possible to use nginx to proxy access to another HTTP server, this
73 option is overkill for our purposes. nginx is itself a fully
74 featured HTTP server, so we will choose in this guide not to make
75 nginx reinterpret Fossil’s implementation of HTTP.
76
77 * **CGI** — This method is simple but inefficient, because it launches
78 a separate Fossil instance on every HTTP hit.
79
80 Since Fossil is a relatively small self-contained program, and it’s
@@ -107,103 +107,74 @@
107 $ sudo apt install fossil nginx
108
109
110 ## <a name="scgi"></a>Running Fossil in SCGI Mode
111
112 I run my Fossil SCGI server instances with a variant of [the `fslsrv`
113 shell script](/file/tools/fslsrv) currently hosted in the Fossil source
114 code repository. You’ll want to download that and make a copy of it, so
115 you can customize it to your particular needs.
116
117 This script allows running multiple Fossil SCGI servers, one per
118 repository, each bound to a different high-numbered `localhost` port, so
119 that only nginx can see and proxy them out to the public. The
120 “`example`” repo is on TCP port localhost:12345, and the “`foo`” repo is
121 on localhost:12346.
122
123 As written, the `fslsrv` script expects repositories to be stored in the
124 calling user’s home directory under `~/museum`, because where else do
125 you keep Fossils?
126
127 That home directory also needs to have a directory to hold log files,
128 `~/log/fossil/*.log`. Fossil doesn’t put out much logging, but when it
129 does, it’s better to have it captured than to need to re-create the
130 problem after the fact.
131
132 The use of `--baseurl` in this script lets us have each Fossil
133 repository mounted in a different location in the URL scheme. Here, for
134 example, we’re saying that the “`example`” repository is hosted under
135 the `/code` URI on its domains, but that the “`foo`” repo is hosted at
136 the top level of its domain. You’ll want to do something like the
137 former for a Fossil repo that’s just one piece of a larger site, but the
138 latter for a repo that is basically the whole point of the site.
139
140 You might also want another script to automate the update, build, and
141 deployment steps for new Fossil versions:
142
143 #!/bin/sh
144 cd $HOME/src/fossil/trunk
145 fossil up
146 make -j11
147 killall fossil
148 sudo make install
149 fslsrv
150
151 The `killall fossil` step is needed only on OSes that refuse to let you
152 replace a running binary on disk.
153
154 As written, the `fslsrv` script assumes a Linux environment. It expects
155 `/bin/bash` to exist, and it depends on non-POSIX tools like `pgrep`.
156 It should not be difficult to port to systems like macOS or the BSDs.
157
158
159 ## <a name="config"></a>Configuration
160
161 On Debian and Ubuntu systems the primary user-level configuration file
162 for nginx is `/etc/nginx/sites-enabled/default`. I recommend that this
163 file contain only a list of include statements, one for each site that
164 server hosts:
165
166 include local/example
167 include local/foo
168
169 Those files then each define one domain’s configuration. Here,
170 `/etc/nginx/local/example` contains the configuration for
171 `*.example.com` and `*.example.net`; and `local/foo` contains the
172 configuration for `*.foo.net`.
173
174 The configuration for our `foo.net` web site, stored in
175 `/etc/nginx/sites-enabled/local/foo` is:
176
177 server {
178 server_name .foo.net;
179 include local/generic;
180
181 access_log /var/log/nginx/foo.net-https-access.log;
182 error_log /var/log/nginx/foo.net-https-error.log;
183
184 # Bypass Fossil for the static Doxygen docs
185 location /doc/html {
186 root /var/www/foo.net;
 
 
 
 
 
187
188 location ~* \.(html|ico|css|js|gif|jpg|png)$ {
189 expires 7d;
190 add_header Vary Accept-Encoding;
191 access_log off;
192 }
193 }
194
195 # Redirect everything else to the Fossil instance
196 location / {
197 include scgi_params;
 
198 scgi_pass 127.0.0.1:12345;
199 scgi_param HTTPS "on";
200 scgi_param SCRIPT_NAME "";
201 }
202 }
203
204 As you can see, this is a simple extension of [the basic nginx service
205 configuration for SCGI][scgii], showing off a few ideas you might want to
206 try on your own site, such as static asset proxying.
207
208 The `local/generic` file referenced above helps us reduce unnecessary
209 repetition among the multiple sites this configuration hosts:
@@ -221,10 +192,34 @@
221 the `access_log` and `error_log` directives, which follow an obvious
222 pattern from one host to the next. Sadly, you must tolerate some
223 repetition across `server { }` blocks when setting up multiple domains
224 on a single server.
225
226 The configuration for `example.com` and `example.net` is similar.
227
228 See [the nginx docs](http://nginx.org/en/docs/) for more ideas.
229
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230 *[Return to the top-level Fossil server article.](../)*
231
--- www/server/debian/nginx.md
+++ www/server/debian/nginx.md
@@ -66,15 +66,15 @@
66
67 Fossil provides four major ways to access a repository it’s serving
68 remotely, three of which are straightforward to use with nginx:
69
70 * **HTTP** — Fossil has a built-in HTTP server: [`fossil
71 server`](../any/none.md). While this method is efficient and it’s
72 possible to use nginx to proxy access to another HTTP server, we
73 don’t see any particularly good reason to make nginx reinterpret
74 Fossil’s own implementation of HTTP when we have a better option.
75 (But see [below](#http).)
76
77 * **CGI** — This method is simple but inefficient, because it launches
78 a separate Fossil instance on every HTTP hit.
79
80 Since Fossil is a relatively small self-contained program, and it’s
@@ -107,103 +107,74 @@
107 $ sudo apt install fossil nginx
108
109
110 ## <a name="scgi"></a>Running Fossil in SCGI Mode
111
112 For the following nginx configuration to work, it needs to contact a
113 Fossil instance speaking the SCGI protocol. There are [many ways](../)
114 to set that up. For Debian type systems, we primarily recommend
115 following [our systemd user service guide](service.md).
116
117 Another option would be to customize [the `fslsrv` shell
118 script](/file/tools/fslsrv) that ships with Fossil as an example of
119 launching multiple Fossil instances in the background to serve multiple
120 URLs.
121
122 However you do it, you need to match up the TCP port numbers between it
123 and those in the nginx configuration below.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
125
126 ## <a name="config"></a>Configuration
127
128 On Debian and Ubuntu systems the primary user-level configuration file
129 for nginx is `/etc/nginx/sites-enabled/default`. I recommend that this
130 file contain only a list of include statements, one for each site that
131 server hosts:
132
133 include local/example.com
134 include local/foo.net
135
136 Those files then each define one domain’s configuration. Here,
137 `/etc/nginx/local/example.com` contains the configuration for
138 `*.example.com` and its alias `*.example.net`; and `local/foo.net`
139 contains the configuration for `*.foo.net`.
140
141 The configuration for our `example.com` web site, stored in
142 `/etc/nginx/sites-enabled/local/example.com` is:
143
144 server {
145 server_name .example.com .example.net "";
146 include local/generic;
147
148 access_log /var/log/nginx/example.com-https-access.log;
149 error_log /var/log/nginx/example.com-https-error.log;
150
151 # Bypass Fossil for the static documentation generated from
152 # our source code by Doxygen, so it merges into the embedded
153 # doc URL hierarchy at Fossil’s $ROOT/doc without requiring that
154 # these generated files actually be stored in the repo. This
155 # also lets us set aggressive caching on these docs, since
156 # they rarely change.
157 location /code/doc/html {
158 root /var/www/example.com/code/doc/html;
159
160 location ~* \.(html|ico|css|js|gif|jpg|png)$ {
161 expires 7d;
162 add_header Vary Accept-Encoding;
163 access_log off;
164 }
165 }
166
167 # Redirect everything else to the Fossil instance
168 location /code {
169 include scgi_params;
170 scgi_param SCRIPT_NAME "/code";
171 scgi_pass 127.0.0.1:12345;
 
 
172 }
173 }
174
175 As you can see, this is a pure extension of [the basic nginx service
176 configuration for SCGI][scgii], showing off a few ideas you might want to
177 try on your own site, such as static asset proxying.
178
179 The `local/generic` file referenced above helps us reduce unnecessary
180 repetition among the multiple sites this configuration hosts:
@@ -221,10 +192,34 @@
192 the `access_log` and `error_log` directives, which follow an obvious
193 pattern from one host to the next. Sadly, you must tolerate some
194 repetition across `server { }` blocks when setting up multiple domains
195 on a single server.
196
197 The configuration for `foo.net` is similar.
198
199 See [the nginx docs](http://nginx.org/en/docs/) for more ideas.
200
201
202 ## <a name="http"></a>Proxying HTTP Anyway
203
204 [Above](#modes), we argued that proxying SCGI is a better option than
205 making nginx reinterpret Fossil’s own implementation of HTTP. If you
206 want Fossil to speak HTTP, just [set Fossil up as a standalone
207 server](../any/none.md). And if you want nginx to [provide TLS
208 encryption for Fossil][tls], proxying HTTP instead of SCGI provides no
209 benefit.
210
211 However, it is still worth showing the proper method of proxying
212 Fossil’s HTTP server through nginx if only to make reading nginx
213 documentation on other sites easier:
214
215 location /code {
216 rewrite ^/code(/.*) $1 break;
217 proxy_pass http://127.0.0.1:12345;
218 }
219
220 The most common thing people get wrong when hand-rolling a configuration
221 like this is to get the slashes wrong. Fossil is senstitive to this. For
222 instance, Fossil will not collapse double slashes down to a single
223 slash, as some other HTTP servers will.
224
225 *[Return to the top-level Fossil server article.](../)*
226
--- www/server/index.html
+++ www/server/index.html
@@ -3,10 +3,14 @@
33
<style type="text/css">
44
p {
55
margin-left: 4em;
66
margin-right: 3em;
77
}
8
+
9
+ li p {
10
+ margin-left: 0;
11
+ }
812
913
h2 {
1014
margin-left: 1em;
1115
}
1216
@@ -52,95 +56,136 @@
5256
5357
5458
<h2>No Server Required</h2>
5559
5660
<p>Fossil does not require a central server, but <a
57
-href="whyuseaserver.wiki">a server can be very useful</a>.</p>
61
+href="whyuseaserver.wiki">a server can be useful</a>.</p>
62
+
63
+<p>A Fossil server does not require much memory, CPU, or disk space
64
+and can run comfortably on a generic $5/month virtual host
65
+or on a small device like a Raspberry Pi, or it can co-exist
66
+on a host running other services without getting in the way.
5867
5968
<p>This article is a quick-reference guide for setting up your own
6069
Fossil server, with links to more detailed instructions specific to
6170
particular systems, should you want extra help.</p>
6271
6372
64
-<h2 id="methods">Methods</h2>
73
+<h2 id="prep">Repository Prep</h2>
74
+
75
+<p>Prior to serving a Fossil repository to others, consider running <a
76
+href="$ROOT/help?cmd=ui"><tt>fossil ui</tt></a> locally and taking these
77
+minimum recommended preparation steps:</p>
78
+
79
+<ol>
80
+ <li><p>Fossil creates only one user in a <a
81
+ href="$ROOT/help?cmd=new">new repository</a> and gives it the <a
82
+ href="../admin-v-setup.md">all-powerful Setup capability</a>. (“s”)
83
+ The 10-digit random password generated for that user is fairly strong
84
+ against remote attack, even without explicit password guess rate
85
+ limiting, but because that user has so much power, you may want to
86
+ give it a much stronger password under Admin → Users.</a></li>
87
+
88
+ <li><p>Run the Admin → Security-Audit tool to verify that other
89
+ security-related permissions and settings are as you want them.
90
+ Consider clicking the “Take it private” link on that page to lock down
91
+ the security on that site to a level appropriate to a private
92
+ repository, even if you will eventually want some public service. It's
93
+ better to start from a secure position and open up service
94
+ feature-by-feature as necessary than it is to start from a fully open
95
+ position and lock down features one by one to achieve a secure
96
+ stance.</p></li>
97
+</ol>
98
+
99
+<p>With the repository secured, it is safe to upload a copy of the
100
+repository file to your server and proceed with server setup, below.
101
+Further configuration steps can wait until <a href="#postsetup">after
102
+the server is running</a>.</p>
103
+
104
+
105
+<h2 id="methods">Activation Methods</h2>
65106
66
-<p>There are basically four ways to set up a Fossil server:</p>
107
+<p>There are basically four ways to run a Fossil server:</p>
67108
68109
<ol>
69110
<li><a id="cgi" href="any/cgi.md">CGI</a>
70
- <li><a id="standalone" href="any/none.md">Stand-alone HTTP server</a>
71111
<li>Socket listener
112
+ <li><a id="standalone" href="any/none.md">Stand-alone HTTP server</a>
72113
<li><a id="scgi" href="any/scgi.md">SCGI</a>
73114
</ol>
74115
75116
<p>All of these methods can serve either a single repository or a
76
-directory containing repositories named "<tt>*.fossil</tt>".</p>
117
+directory hierarchy containing mulitiple repositories.</p>
77118
78
-<p>You are not restricted to using a single method. The same Fossil
119
+<p>You are not restricted to a single server setup. The same Fossil
79120
repository can be served using two or more of the above techniques at
80121
the same time. These methods use clean, well-defined, standard
81122
interfaces (CGI, SCGI, and HTTP) which allow you to easily migrate from
82
-one method to another to accommodate changes in hosting providers or
123
+one method to another in response to changes in hosting providers or
83124
administrator preferences.</p>
84125
85126
<h3>CGI</h3>
86127
87128
<p>Most ordinary web servers can <a href="any/cgi.md">run Fossil as a
88129
CGI script</a>. This method is known to work with Apache,
89130
<tt>lighttpd</tt>, and <a
90131
href="any/althttpd.md"><tt>althttpd</tt></a>. The Fossil server
91
-administrator places a <a href="/help?cmd=cgi">short CGI script</a> in
92
-the web server's document hierarchy, and when a client requests the
93
-appropriate URL, that script runs Fossil to generate the response.</p>
132
+administrator places a <a href="$ROOT/help?cmd=cgi">short CGI script</a> in
133
+the web server's document hierarchy and when a client requests the URL
134
+that corresponds to that script, Fossil runs and generates the
135
+response.</p>
94136
95137
<p>CGI is a good choice for merging Fossil into an existing web site,
96
-particularly on hosts that have CGI set up for you already and won't let
97
-you modify the web server configuration further. The Fossil <a
98
-href="../selfhost.wiki">self-hosting repositories</a> are implemented
99
-with CGI underneath <tt>althttpd</tt>.</p>
138
+particularly on hosts that have CGI set up and working.
139
+The Fossil <a href="../selfhost.wiki">self-hosting repositories</a> are
140
+implemented with CGI underneath <tt>althttpd</tt>.</p>
141
+
142
+<h3>Socket Listener</h3>
143
+
144
+<p>Socket listener daemons such as
145
+<a id="inetd" href="any/inetd.md"><tt>inetd</tt></a>, <a id="xinetd"
146
+href="any/xinetd.md"><tt>xinetd</tt></a>, <a id="stunnel"
147
+href="any/stunnel.md"><tt>stunnel</tt></a>, <a
148
+href="macos/service.md"><tt>launchd</tt></a>, and <a
149
+href="debian/service.md"><tt>systemd</tt></a>
150
+can be configured to invoke the the
151
+<a href="$ROOT/help?cmd=http"><tt>fossil http</tt></a> command to handle
152
+each incoming HTTP request. The "<tt>fossil http</tt>" command reads
153
+the HTTP request off of standard input, computes an appropriate
154
+reply, and writes the reply on standard output. There is a separate
155
+invocation of the "<tt>fossil http</tt>" command for each HTTP request.
156
+The socket listener daemon takes care of relaying content to and from
157
+the client, and (in the case of <a href="any/stunnel.md">stunnel</a>)
158
+handling TLS decryption and encryption.
100159
101160
<h3>Stand-alone HTTP Server</h3>
102161
103162
<p>This is the <a href="any/none.md">easiest method</a>.
104
-A stand-alone server uses the <a
105
-href="/help?cmd=server"><tt>fossil server</tt></a> command to run a
163
+A stand-alone server uses the
164
+<a href="$ROOT/help?cmd=server"><tt>fossil server</tt></a> command to run a
106165
process that listens for incoming HTTP requests on a socket and then
107166
dispatches a copy of itself to deal with each incoming request. You can
108167
expose Fossil directly to the clients in this way or you can interpose a
109168
<a href="https://en.wikipedia.org/wiki/Reverse_proxy">reverse proxy</a>
110169
layer between the clients and Fossil.</p>
111170
112
-<h3>Socket Listener</h3>
113
-
114
-<p>Only slightly more complicated is the socket listener method.
115
-Instead of letting Fossil run in the background continuously to handle
116
-HTTP requests from clients, you configure a socket listener daemon to
117
-run a <a href="/help?cmd=http"><tt>fossil http</tt></a> command on each
118
-HTTP hit. That Fossil instance handles only that one hit and then shuts
119
-back down, letting the socket listener go back to waiting for the next
120
-hit. This scheme is known to work with <a id="inetd"
121
-href="any/inetd.md"><tt>inetd</tt></a>, <a id="xinetd"
122
-href="any/xinetd.md"><tt>xinetd</tt></a>, <a id="stunnel"
123
-href="any/stunnel.md"><tt>stunnel</tt></a>, <a
124
-href="macos/service.md"><tt>launchd</tt></a>, and <a
125
-href="debian/service.md"><tt>systemd</tt></a>.</p>
126
-
127171
<h3>SCGI</h3>
128172
129
-<p>The Fossil standalone server can also run <a href="any/scgi.md">in
130
-SCGI mode</a> — <a href="/help/server"><tt>fossil server --scgi</tt></a>
131
-— instead of <a href="any/none.md">HTTP mode</a>, which allows it to
173
+<p>The Fossil standalone server can also handle <a href="any/scgi.md">SCGI</a>.
174
+When the <a href="$ROOT/help?cmd=server"><tt>fossil server</tt></a> command is
175
+run with the extra <tt>--scgi</tt> option, it listens for incoming
176
+SCGI requests rather than HTTP requests. This allows Fossil to
132177
respond to requests from web servers <a href="debian/nginx.md">such as
133178
nginx</a> that don't support CGI. SCGI is a simpler protocol to proxy
134179
than HTTP, since the HTTP doesn't have to be re-interpreted in terms of
135180
the proxy's existing HTTP implementation, but it's more complex to set
136181
up because you also have to set up an SCGI-to-HTTP proxy for it. It is
137182
worth taking on this difficulty only when you need to integrate Fossil
138183
into an existing web site already being served by an SCGI-capable web
139184
server.</p>
140185
141
-<h2 id="matrix">Setup Tutorials</h2>
186
+<h2 id="matrix">Activation Tutorials</h2>
142187
143188
<p>We've broken the configuration for each method out into a series of
144189
sub-articles. Some of these are generic, while others depend on
145190
particular operating systems or front-end software:</p>
146191
@@ -221,18 +266,59 @@
221266
222267
<p>We welcome <a href="../contribute.wiki">contributions</a> to fill gaps
223268
(<font size="-2">❌</font>) in the table above.</p>
224269
</noscript>
225270
271
+
272
+<h2 id="postsetup">Post-Activation Configuration</h2>
273
+
274
+<p>After the server is up and running, log into it as the Setup user and
275
+visit the Admin menu to finish configuring that repository for
276
+service:</p>
277
+
278
+<ol>
279
+ <li><p>Add user accounts for your other team members. Use the
280
+ pre-defined user capabilities to define access policies rather than
281
+ give out those same set of capabilities redundantly to each
282
+ user.</p></li>
283
+
284
+ <li><p>Test access to the repository from each category of non-Setup
285
+ user that you created. You may have to give your user categories some
286
+ overlooked capabilities, particularly if you followed <a
287
+ href="#prep">our earlier advice</a> to take the repository private
288
+ prior to setting up the server.</p></li>
289
+
290
+ <li><p>Modify the repository's look and feel by <a
291
+ href="../customskin.md">customizing the skin</a>.</p></li>
292
+
293
+ <li><p>If the repository includes <a
294
+ href="../embeddeddoc.wiki">embedded documentation</a>, consider
295
+ activating the search feature (Admin → Search) so that visitors can do
296
+ full-text search on your documentation.</p></li>
297
+
298
+ <li><p>Now that others can be making changes to the repository,
299
+ consider monitoring them via <a href="../alerts.md">email alerts</a>
300
+ or the <a href="$ROOT/help?cmd=/timeline.rss">timeline RSS
301
+ feed</a>.</p></li>
302
+
303
+ <li><p>Turn on the various logging features.</p></li>
304
+</ol>
305
+
306
+<p>Reload the Admin → Security-Audit page occasionally during this
307
+process to double check that you have not mistakenly configured the
308
+server in a way that might expose information that you want to keep
309
+private.</p>
310
+
226311
227312
<h2 id="more">Further Details</h2>
228313
229314
<ul>
230315
<li><a id="chroot" href="../chroot.md" >The Server Chroot Jail</a>
231316
<li><a id="loadmgmt" href="../loadmgmt.md" >Managing Server Load</a>
317
+ <li><a id="bkofc" href="../backoffice.md" >The Backoffice</a>
232318
<li><a id="tls" href="../ssl.wiki" >Securing a Repository with TLS</a>
233319
<li><a id="ext" href="../serverext.wiki">CGI Server Extensions</a>
234320
<li><a id="about" href="../aboutcgi.wiki" >How CGI Works In Fossil</a>
235321
<li><a id="sync" href="../sync.wiki" >The Fossil Sync Protocol</a>
236322
</ul>
237323
238324
</div>
239325
--- www/server/index.html
+++ www/server/index.html
@@ -3,10 +3,14 @@
3 <style type="text/css">
4 p {
5 margin-left: 4em;
6 margin-right: 3em;
7 }
 
 
 
 
8
9 h2 {
10 margin-left: 1em;
11 }
12
@@ -52,95 +56,136 @@
52
53
54 <h2>No Server Required</h2>
55
56 <p>Fossil does not require a central server, but <a
57 href="whyuseaserver.wiki">a server can be very useful</a>.</p>
 
 
 
 
 
58
59 <p>This article is a quick-reference guide for setting up your own
60 Fossil server, with links to more detailed instructions specific to
61 particular systems, should you want extra help.</p>
62
63
64 <h2 id="methods">Methods</h2>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
66 <p>There are basically four ways to set up a Fossil server:</p>
67
68 <ol>
69 <li><a id="cgi" href="any/cgi.md">CGI</a>
70 <li><a id="standalone" href="any/none.md">Stand-alone HTTP server</a>
71 <li>Socket listener
 
72 <li><a id="scgi" href="any/scgi.md">SCGI</a>
73 </ol>
74
75 <p>All of these methods can serve either a single repository or a
76 directory containing repositories named "<tt>*.fossil</tt>".</p>
77
78 <p>You are not restricted to using a single method. The same Fossil
79 repository can be served using two or more of the above techniques at
80 the same time. These methods use clean, well-defined, standard
81 interfaces (CGI, SCGI, and HTTP) which allow you to easily migrate from
82 one method to another to accommodate changes in hosting providers or
83 administrator preferences.</p>
84
85 <h3>CGI</h3>
86
87 <p>Most ordinary web servers can <a href="any/cgi.md">run Fossil as a
88 CGI script</a>. This method is known to work with Apache,
89 <tt>lighttpd</tt>, and <a
90 href="any/althttpd.md"><tt>althttpd</tt></a>. The Fossil server
91 administrator places a <a href="/help?cmd=cgi">short CGI script</a> in
92 the web server's document hierarchy, and when a client requests the
93 appropriate URL, that script runs Fossil to generate the response.</p>
 
94
95 <p>CGI is a good choice for merging Fossil into an existing web site,
96 particularly on hosts that have CGI set up for you already and won't let
97 you modify the web server configuration further. The Fossil <a
98 href="../selfhost.wiki">self-hosting repositories</a> are implemented
99 with CGI underneath <tt>althttpd</tt>.</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
101 <h3>Stand-alone HTTP Server</h3>
102
103 <p>This is the <a href="any/none.md">easiest method</a>.
104 A stand-alone server uses the <a
105 href="/help?cmd=server"><tt>fossil server</tt></a> command to run a
106 process that listens for incoming HTTP requests on a socket and then
107 dispatches a copy of itself to deal with each incoming request. You can
108 expose Fossil directly to the clients in this way or you can interpose a
109 <a href="https://en.wikipedia.org/wiki/Reverse_proxy">reverse proxy</a>
110 layer between the clients and Fossil.</p>
111
112 <h3>Socket Listener</h3>
113
114 <p>Only slightly more complicated is the socket listener method.
115 Instead of letting Fossil run in the background continuously to handle
116 HTTP requests from clients, you configure a socket listener daemon to
117 run a <a href="/help?cmd=http"><tt>fossil http</tt></a> command on each
118 HTTP hit. That Fossil instance handles only that one hit and then shuts
119 back down, letting the socket listener go back to waiting for the next
120 hit. This scheme is known to work with <a id="inetd"
121 href="any/inetd.md"><tt>inetd</tt></a>, <a id="xinetd"
122 href="any/xinetd.md"><tt>xinetd</tt></a>, <a id="stunnel"
123 href="any/stunnel.md"><tt>stunnel</tt></a>, <a
124 href="macos/service.md"><tt>launchd</tt></a>, and <a
125 href="debian/service.md"><tt>systemd</tt></a>.</p>
126
127 <h3>SCGI</h3>
128
129 <p>The Fossil standalone server can also run <a href="any/scgi.md">in
130 SCGI mode</a> — <a href="/help/server"><tt>fossil server --scgi</tt></a>
131 — instead of <a href="any/none.md">HTTP mode</a>, which allows it to
 
132 respond to requests from web servers <a href="debian/nginx.md">such as
133 nginx</a> that don't support CGI. SCGI is a simpler protocol to proxy
134 than HTTP, since the HTTP doesn't have to be re-interpreted in terms of
135 the proxy's existing HTTP implementation, but it's more complex to set
136 up because you also have to set up an SCGI-to-HTTP proxy for it. It is
137 worth taking on this difficulty only when you need to integrate Fossil
138 into an existing web site already being served by an SCGI-capable web
139 server.</p>
140
141 <h2 id="matrix">Setup Tutorials</h2>
142
143 <p>We've broken the configuration for each method out into a series of
144 sub-articles. Some of these are generic, while others depend on
145 particular operating systems or front-end software:</p>
146
@@ -221,18 +266,59 @@
221
222 <p>We welcome <a href="../contribute.wiki">contributions</a> to fill gaps
223 (<font size="-2">❌</font>) in the table above.</p>
224 </noscript>
225
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
227 <h2 id="more">Further Details</h2>
228
229 <ul>
230 <li><a id="chroot" href="../chroot.md" >The Server Chroot Jail</a>
231 <li><a id="loadmgmt" href="../loadmgmt.md" >Managing Server Load</a>
 
232 <li><a id="tls" href="../ssl.wiki" >Securing a Repository with TLS</a>
233 <li><a id="ext" href="../serverext.wiki">CGI Server Extensions</a>
234 <li><a id="about" href="../aboutcgi.wiki" >How CGI Works In Fossil</a>
235 <li><a id="sync" href="../sync.wiki" >The Fossil Sync Protocol</a>
236 </ul>
237
238 </div>
239
--- www/server/index.html
+++ www/server/index.html
@@ -3,10 +3,14 @@
3 <style type="text/css">
4 p {
5 margin-left: 4em;
6 margin-right: 3em;
7 }
8
9 li p {
10 margin-left: 0;
11 }
12
13 h2 {
14 margin-left: 1em;
15 }
16
@@ -52,95 +56,136 @@
56
57
58 <h2>No Server Required</h2>
59
60 <p>Fossil does not require a central server, but <a
61 href="whyuseaserver.wiki">a server can be useful</a>.</p>
62
63 <p>A Fossil server does not require much memory, CPU, or disk space
64 and can run comfortably on a generic $5/month virtual host
65 or on a small device like a Raspberry Pi, or it can co-exist
66 on a host running other services without getting in the way.
67
68 <p>This article is a quick-reference guide for setting up your own
69 Fossil server, with links to more detailed instructions specific to
70 particular systems, should you want extra help.</p>
71
72
73 <h2 id="prep">Repository Prep</h2>
74
75 <p>Prior to serving a Fossil repository to others, consider running <a
76 href="$ROOT/help?cmd=ui"><tt>fossil ui</tt></a> locally and taking these
77 minimum recommended preparation steps:</p>
78
79 <ol>
80 <li><p>Fossil creates only one user in a <a
81 href="$ROOT/help?cmd=new">new repository</a> and gives it the <a
82 href="../admin-v-setup.md">all-powerful Setup capability</a>. (“s”)
83 The 10-digit random password generated for that user is fairly strong
84 against remote attack, even without explicit password guess rate
85 limiting, but because that user has so much power, you may want to
86 give it a much stronger password under Admin → Users.</a></li>
87
88 <li><p>Run the Admin → Security-Audit tool to verify that other
89 security-related permissions and settings are as you want them.
90 Consider clicking the “Take it private” link on that page to lock down
91 the security on that site to a level appropriate to a private
92 repository, even if you will eventually want some public service. It's
93 better to start from a secure position and open up service
94 feature-by-feature as necessary than it is to start from a fully open
95 position and lock down features one by one to achieve a secure
96 stance.</p></li>
97 </ol>
98
99 <p>With the repository secured, it is safe to upload a copy of the
100 repository file to your server and proceed with server setup, below.
101 Further configuration steps can wait until <a href="#postsetup">after
102 the server is running</a>.</p>
103
104
105 <h2 id="methods">Activation Methods</h2>
106
107 <p>There are basically four ways to run a Fossil server:</p>
108
109 <ol>
110 <li><a id="cgi" href="any/cgi.md">CGI</a>
 
111 <li>Socket listener
112 <li><a id="standalone" href="any/none.md">Stand-alone HTTP server</a>
113 <li><a id="scgi" href="any/scgi.md">SCGI</a>
114 </ol>
115
116 <p>All of these methods can serve either a single repository or a
117 directory hierarchy containing mulitiple repositories.</p>
118
119 <p>You are not restricted to a single server setup. The same Fossil
120 repository can be served using two or more of the above techniques at
121 the same time. These methods use clean, well-defined, standard
122 interfaces (CGI, SCGI, and HTTP) which allow you to easily migrate from
123 one method to another in response to changes in hosting providers or
124 administrator preferences.</p>
125
126 <h3>CGI</h3>
127
128 <p>Most ordinary web servers can <a href="any/cgi.md">run Fossil as a
129 CGI script</a>. This method is known to work with Apache,
130 <tt>lighttpd</tt>, and <a
131 href="any/althttpd.md"><tt>althttpd</tt></a>. The Fossil server
132 administrator places a <a href="$ROOT/help?cmd=cgi">short CGI script</a> in
133 the web server's document hierarchy and when a client requests the URL
134 that corresponds to that script, Fossil runs and generates the
135 response.</p>
136
137 <p>CGI is a good choice for merging Fossil into an existing web site,
138 particularly on hosts that have CGI set up and working.
139 The Fossil <a href="../selfhost.wiki">self-hosting repositories</a> are
140 implemented with CGI underneath <tt>althttpd</tt>.</p>
141
142 <h3>Socket Listener</h3>
143
144 <p>Socket listener daemons such as
145 <a id="inetd" href="any/inetd.md"><tt>inetd</tt></a>, <a id="xinetd"
146 href="any/xinetd.md"><tt>xinetd</tt></a>, <a id="stunnel"
147 href="any/stunnel.md"><tt>stunnel</tt></a>, <a
148 href="macos/service.md"><tt>launchd</tt></a>, and <a
149 href="debian/service.md"><tt>systemd</tt></a>
150 can be configured to invoke the the
151 <a href="$ROOT/help?cmd=http"><tt>fossil http</tt></a> command to handle
152 each incoming HTTP request. The "<tt>fossil http</tt>" command reads
153 the HTTP request off of standard input, computes an appropriate
154 reply, and writes the reply on standard output. There is a separate
155 invocation of the "<tt>fossil http</tt>" command for each HTTP request.
156 The socket listener daemon takes care of relaying content to and from
157 the client, and (in the case of <a href="any/stunnel.md">stunnel</a>)
158 handling TLS decryption and encryption.
159
160 <h3>Stand-alone HTTP Server</h3>
161
162 <p>This is the <a href="any/none.md">easiest method</a>.
163 A stand-alone server uses the
164 <a href="$ROOT/help?cmd=server"><tt>fossil server</tt></a> command to run a
165 process that listens for incoming HTTP requests on a socket and then
166 dispatches a copy of itself to deal with each incoming request. You can
167 expose Fossil directly to the clients in this way or you can interpose a
168 <a href="https://en.wikipedia.org/wiki/Reverse_proxy">reverse proxy</a>
169 layer between the clients and Fossil.</p>
170
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171 <h3>SCGI</h3>
172
173 <p>The Fossil standalone server can also handle <a href="any/scgi.md">SCGI</a>.
174 When the <a href="$ROOT/help?cmd=server"><tt>fossil server</tt></a> command is
175 run with the extra <tt>--scgi</tt> option, it listens for incoming
176 SCGI requests rather than HTTP requests. This allows Fossil to
177 respond to requests from web servers <a href="debian/nginx.md">such as
178 nginx</a> that don't support CGI. SCGI is a simpler protocol to proxy
179 than HTTP, since the HTTP doesn't have to be re-interpreted in terms of
180 the proxy's existing HTTP implementation, but it's more complex to set
181 up because you also have to set up an SCGI-to-HTTP proxy for it. It is
182 worth taking on this difficulty only when you need to integrate Fossil
183 into an existing web site already being served by an SCGI-capable web
184 server.</p>
185
186 <h2 id="matrix">Activation Tutorials</h2>
187
188 <p>We've broken the configuration for each method out into a series of
189 sub-articles. Some of these are generic, while others depend on
190 particular operating systems or front-end software:</p>
191
@@ -221,18 +266,59 @@
266
267 <p>We welcome <a href="../contribute.wiki">contributions</a> to fill gaps
268 (<font size="-2">❌</font>) in the table above.</p>
269 </noscript>
270
271
272 <h2 id="postsetup">Post-Activation Configuration</h2>
273
274 <p>After the server is up and running, log into it as the Setup user and
275 visit the Admin menu to finish configuring that repository for
276 service:</p>
277
278 <ol>
279 <li><p>Add user accounts for your other team members. Use the
280 pre-defined user capabilities to define access policies rather than
281 give out those same set of capabilities redundantly to each
282 user.</p></li>
283
284 <li><p>Test access to the repository from each category of non-Setup
285 user that you created. You may have to give your user categories some
286 overlooked capabilities, particularly if you followed <a
287 href="#prep">our earlier advice</a> to take the repository private
288 prior to setting up the server.</p></li>
289
290 <li><p>Modify the repository's look and feel by <a
291 href="../customskin.md">customizing the skin</a>.</p></li>
292
293 <li><p>If the repository includes <a
294 href="../embeddeddoc.wiki">embedded documentation</a>, consider
295 activating the search feature (Admin → Search) so that visitors can do
296 full-text search on your documentation.</p></li>
297
298 <li><p>Now that others can be making changes to the repository,
299 consider monitoring them via <a href="../alerts.md">email alerts</a>
300 or the <a href="$ROOT/help?cmd=/timeline.rss">timeline RSS
301 feed</a>.</p></li>
302
303 <li><p>Turn on the various logging features.</p></li>
304 </ol>
305
306 <p>Reload the Admin → Security-Audit page occasionally during this
307 process to double check that you have not mistakenly configured the
308 server in a way that might expose information that you want to keep
309 private.</p>
310
311
312 <h2 id="more">Further Details</h2>
313
314 <ul>
315 <li><a id="chroot" href="../chroot.md" >The Server Chroot Jail</a>
316 <li><a id="loadmgmt" href="../loadmgmt.md" >Managing Server Load</a>
317 <li><a id="bkofc" href="../backoffice.md" >The Backoffice</a>
318 <li><a id="tls" href="../ssl.wiki" >Securing a Repository with TLS</a>
319 <li><a id="ext" href="../serverext.wiki">CGI Server Extensions</a>
320 <li><a id="about" href="../aboutcgi.wiki" >How CGI Works In Fossil</a>
321 <li><a id="sync" href="../sync.wiki" >The Fossil Sync Protocol</a>
322 </ul>
323
324 </div>
325
--- www/server/whyuseaserver.wiki
+++ www/server/whyuseaserver.wiki
@@ -1,10 +1,10 @@
11
<title>Benefits Of A Fossil Server</title>
22
33
<h2>No Server Required</h2>
44
5
-Fossil does <em>not</em> require a central server.
5
+Fossil does not require a central server.
66
Data sharing and synchronization can be entirely peer-to-peer.
77
Fossil uses
88
[https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type|conflict-free replicated data types]
99
to ensure that (in the limit) all participating peers see the same content.
1010
@@ -11,27 +11,30 @@
1111
<h2>But, A Server Can Be Useful</h2>
1212
1313
Fossil does not require a server, but a server can be very useful.
1414
Here are a few reasons to set up a Fossil server for your project:
1515
16
- 1. <b>A server gives developers a common point of rendezvous for
16
+ 1. <b>A server works as a complete project website.</b><p>
17
+ Fossil does more than just version control. It also supports
18
+ [../tickets.wiki|trouble-tickets],
19
+ [../wikitheory.wiki|wiki], and a [../forum.wiki|forum].
20
+ The [../embeddeddoc.wiki|embedded documentation]
21
+ feature provides a great mechanism for providing project documentation.
22
+ The [../unvers.wiki|unversioned files] feature is a convenient way
23
+ to host builds and downloads on the project website.
24
+
25
+ 2. <b>A server gives developers a common point of rendezvous for
1726
syncing their work.</b><p>
18
- It is possible for developers to synchronous peer-to-peer but
27
+ It is possible for developers to synchronize peer-to-peer but
1928
that requires the developers coordinate the sync, which in turn
2029
requires that the developers both want to sync at the same moment.
2130
A server aleviates this time dependency by allowing each developer
2231
to sync whenever it is convenient (for example, automatically syncing
2332
after each commit and before each update). Developers all stay
2433
in sync with each other, without having to interrupt each other
2534
constantly to set up a peer-to-peer sync.
2635
27
- 2. <b>A server works as a project website for non-developers.</b><p>
28
- Fossil does more than just version control. It also supports
29
- trouble-tickets, and wiki, and a forum. It shows the status
30
- of the project. And the embedded documentation feature provides
31
- a great mechanism for providing only instructions.
32
-
3336
3. <b>A server provides project leaders with up-to-date status.</b><p>
3437
Project coordinators and BDFLs can click on a link or two at the
3538
central Fossil server for a project, and quickly tell what is
3639
going on. They can do this from anywhere, even from their phones,
3740
without needing to actually sync to the device they are using.
@@ -39,6 +42,6 @@
3942
4. <b>A server provides automatic off-site backups.</b><p>
4043
A Fossil server is an automatic remote backup for all the work
4144
going into a project. You can even set up multiple servers, at
4245
multiple sites, with automatic synchronization between them, for
4346
added redundancy. Such a set up means that no work is lost due
44
- to a single machine failure.
47
+ to a single machine failur
4548
--- www/server/whyuseaserver.wiki
+++ www/server/whyuseaserver.wiki
@@ -1,10 +1,10 @@
1 <title>Benefits Of A Fossil Server</title>
2
3 <h2>No Server Required</h2>
4
5 Fossil does <em>not</em> require a central server.
6 Data sharing and synchronization can be entirely peer-to-peer.
7 Fossil uses
8 [https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type|conflict-free replicated data types]
9 to ensure that (in the limit) all participating peers see the same content.
10
@@ -11,27 +11,30 @@
11 <h2>But, A Server Can Be Useful</h2>
12
13 Fossil does not require a server, but a server can be very useful.
14 Here are a few reasons to set up a Fossil server for your project:
15
16 1. <b>A server gives developers a common point of rendezvous for
 
 
 
 
 
 
 
 
 
17 syncing their work.</b><p>
18 It is possible for developers to synchronous peer-to-peer but
19 that requires the developers coordinate the sync, which in turn
20 requires that the developers both want to sync at the same moment.
21 A server aleviates this time dependency by allowing each developer
22 to sync whenever it is convenient (for example, automatically syncing
23 after each commit and before each update). Developers all stay
24 in sync with each other, without having to interrupt each other
25 constantly to set up a peer-to-peer sync.
26
27 2. <b>A server works as a project website for non-developers.</b><p>
28 Fossil does more than just version control. It also supports
29 trouble-tickets, and wiki, and a forum. It shows the status
30 of the project. And the embedded documentation feature provides
31 a great mechanism for providing only instructions.
32
33 3. <b>A server provides project leaders with up-to-date status.</b><p>
34 Project coordinators and BDFLs can click on a link or two at the
35 central Fossil server for a project, and quickly tell what is
36 going on. They can do this from anywhere, even from their phones,
37 without needing to actually sync to the device they are using.
@@ -39,6 +42,6 @@
39 4. <b>A server provides automatic off-site backups.</b><p>
40 A Fossil server is an automatic remote backup for all the work
41 going into a project. You can even set up multiple servers, at
42 multiple sites, with automatic synchronization between them, for
43 added redundancy. Such a set up means that no work is lost due
44 to a single machine failure.
45
--- www/server/whyuseaserver.wiki
+++ www/server/whyuseaserver.wiki
@@ -1,10 +1,10 @@
1 <title>Benefits Of A Fossil Server</title>
2
3 <h2>No Server Required</h2>
4
5 Fossil does not require a central server.
6 Data sharing and synchronization can be entirely peer-to-peer.
7 Fossil uses
8 [https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type|conflict-free replicated data types]
9 to ensure that (in the limit) all participating peers see the same content.
10
@@ -11,27 +11,30 @@
11 <h2>But, A Server Can Be Useful</h2>
12
13 Fossil does not require a server, but a server can be very useful.
14 Here are a few reasons to set up a Fossil server for your project:
15
16 1. <b>A server works as a complete project website.</b><p>
17 Fossil does more than just version control. It also supports
18 [../tickets.wiki|trouble-tickets],
19 [../wikitheory.wiki|wiki], and a [../forum.wiki|forum].
20 The [../embeddeddoc.wiki|embedded documentation]
21 feature provides a great mechanism for providing project documentation.
22 The [../unvers.wiki|unversioned files] feature is a convenient way
23 to host builds and downloads on the project website.
24
25 2. <b>A server gives developers a common point of rendezvous for
26 syncing their work.</b><p>
27 It is possible for developers to synchronize peer-to-peer but
28 that requires the developers coordinate the sync, which in turn
29 requires that the developers both want to sync at the same moment.
30 A server aleviates this time dependency by allowing each developer
31 to sync whenever it is convenient (for example, automatically syncing
32 after each commit and before each update). Developers all stay
33 in sync with each other, without having to interrupt each other
34 constantly to set up a peer-to-peer sync.
35
 
 
 
 
 
 
36 3. <b>A server provides project leaders with up-to-date status.</b><p>
37 Project coordinators and BDFLs can click on a link or two at the
38 central Fossil server for a project, and quickly tell what is
39 going on. They can do this from anywhere, even from their phones,
40 without needing to actually sync to the device they are using.
@@ -39,6 +42,6 @@
42 4. <b>A server provides automatic off-site backups.</b><p>
43 A Fossil server is an automatic remote backup for all the work
44 going into a project. You can even set up multiple servers, at
45 multiple sites, with automatic synchronization between them, for
46 added redundancy. Such a set up means that no work is lost due
47 to a single machine failur
48

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button