├── .gitignore
├── LICENSE
├── bootstrap.yml
├── castle
├── docker-compose.yml
├── letterbook.Dockerfile
├── letterbook.compose.yml
├── mastodon-streaming.Dockerfile
├── mastodon.Dockerfile
├── mastodon.compose.yml
├── network-tools.Dockerfile
├── pasture.Dockerfile
├── pasture.castle.yml
├── readme.md
├── sharkey.compose.yml~
├── templates
├── Dockerfile
└── compose.yml
├── traefik.Dockerfile
├── units
├── redir443.service
└── redir80.service
└── volumes
├── ca.json
├── hosts
├── mastodon
└── init.sh
├── pasture
└── .gitignore
├── postgresql.conf
├── proxy
├── acme.json
├── traefik.yml
└── traefik_dynamic.yml
├── root-ca
├── .gitignore
└── readme.md
└── sharkey
├── default.yml
└── docker.env
/.gitignore:
--------------------------------------------------------------------------------
1 | *.backup.*
2 | .idea/
3 | .env
4 | *local*
5 | volumes/containers.conf
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Mozilla Public License Version 2.0
2 | ==================================
3 |
4 | 1. Definitions
5 | --------------
6 |
7 | 1.1. "Contributor"
8 | means each individual or legal entity that creates, contributes to
9 | the creation of, or owns Covered Software.
10 |
11 | 1.2. "Contributor Version"
12 | means the combination of the Contributions of others (if any) used
13 | by a Contributor and that particular Contributor's Contribution.
14 |
15 | 1.3. "Contribution"
16 | means Covered Software of a particular Contributor.
17 |
18 | 1.4. "Covered Software"
19 | means Source Code Form to which the initial Contributor has attached
20 | the notice in Exhibit A, the Executable Form of such Source Code
21 | Form, and Modifications of such Source Code Form, in each case
22 | including portions thereof.
23 |
24 | 1.5. "Incompatible With Secondary Licenses"
25 | means
26 |
27 | (a) that the initial Contributor has attached the notice described
28 | in Exhibit B to the Covered Software; or
29 |
30 | (b) that the Covered Software was made available under the terms of
31 | version 1.1 or earlier of the License, but not also under the
32 | terms of a Secondary License.
33 |
34 | 1.6. "Executable Form"
35 | means any form of the work other than Source Code Form.
36 |
37 | 1.7. "Larger Work"
38 | means a work that combines Covered Software with other material, in
39 | a separate file or files, that is not Covered Software.
40 |
41 | 1.8. "License"
42 | means this document.
43 |
44 | 1.9. "Licensable"
45 | means having the right to grant, to the maximum extent possible,
46 | whether at the time of the initial grant or subsequently, any and
47 | all of the rights conveyed by this License.
48 |
49 | 1.10. "Modifications"
50 | means any of the following:
51 |
52 | (a) any file in Source Code Form that results from an addition to,
53 | deletion from, or modification of the contents of Covered
54 | Software; or
55 |
56 | (b) any new file in Source Code Form that contains any Covered
57 | Software.
58 |
59 | 1.11. "Patent Claims" of a Contributor
60 | means any patent claim(s), including without limitation, method,
61 | process, and apparatus claims, in any patent Licensable by such
62 | Contributor that would be infringed, but for the grant of the
63 | License, by the making, using, selling, offering for sale, having
64 | made, import, or transfer of either its Contributions or its
65 | Contributor Version.
66 |
67 | 1.12. "Secondary License"
68 | means either the GNU General Public License, Version 2.0, the GNU
69 | Lesser General Public License, Version 2.1, the GNU Affero General
70 | Public License, Version 3.0, or any later versions of those
71 | licenses.
72 |
73 | 1.13. "Source Code Form"
74 | means the form of the work preferred for making modifications.
75 |
76 | 1.14. "You" (or "Your")
77 | means an individual or a legal entity exercising rights under this
78 | License. For legal entities, "You" includes any entity that
79 | controls, is controlled by, or is under common control with You. For
80 | purposes of this definition, "control" means (a) the power, direct
81 | or indirect, to cause the direction or management of such entity,
82 | whether by contract or otherwise, or (b) ownership of more than
83 | fifty percent (50%) of the outstanding shares or beneficial
84 | ownership of such entity.
85 |
86 | 2. License Grants and Conditions
87 | --------------------------------
88 |
89 | 2.1. Grants
90 |
91 | Each Contributor hereby grants You a world-wide, royalty-free,
92 | non-exclusive license:
93 |
94 | (a) under intellectual property rights (other than patent or trademark)
95 | Licensable by such Contributor to use, reproduce, make available,
96 | modify, display, perform, distribute, and otherwise exploit its
97 | Contributions, either on an unmodified basis, with Modifications, or
98 | as part of a Larger Work; and
99 |
100 | (b) under Patent Claims of such Contributor to make, use, sell, offer
101 | for sale, have made, import, and otherwise transfer either its
102 | Contributions or its Contributor Version.
103 |
104 | 2.2. Effective Date
105 |
106 | The licenses granted in Section 2.1 with respect to any Contribution
107 | become effective for each Contribution on the date the Contributor first
108 | distributes such Contribution.
109 |
110 | 2.3. Limitations on Grant Scope
111 |
112 | The licenses granted in this Section 2 are the only rights granted under
113 | this License. No additional rights or licenses will be implied from the
114 | distribution or licensing of Covered Software under this License.
115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a
116 | Contributor:
117 |
118 | (a) for any code that a Contributor has removed from Covered Software;
119 | or
120 |
121 | (b) for infringements caused by: (i) Your and any other third party's
122 | modifications of Covered Software, or (ii) the combination of its
123 | Contributions with other software (except as part of its Contributor
124 | Version); or
125 |
126 | (c) under Patent Claims infringed by Covered Software in the absence of
127 | its Contributions.
128 |
129 | This License does not grant any rights in the trademarks, service marks,
130 | or logos of any Contributor (except as may be necessary to comply with
131 | the notice requirements in Section 3.4).
132 |
133 | 2.4. Subsequent Licenses
134 |
135 | No Contributor makes additional grants as a result of Your choice to
136 | distribute the Covered Software under a subsequent version of this
137 | License (see Section 10.2) or under the terms of a Secondary License (if
138 | permitted under the terms of Section 3.3).
139 |
140 | 2.5. Representation
141 |
142 | Each Contributor represents that the Contributor believes its
143 | Contributions are its original creation(s) or it has sufficient rights
144 | to grant the rights to its Contributions conveyed by this License.
145 |
146 | 2.6. Fair Use
147 |
148 | This License is not intended to limit any rights You have under
149 | applicable copyright doctrines of fair use, fair dealing, or other
150 | equivalents.
151 |
152 | 2.7. Conditions
153 |
154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
155 | in Section 2.1.
156 |
157 | 3. Responsibilities
158 | -------------------
159 |
160 | 3.1. Distribution of Source Form
161 |
162 | All distribution of Covered Software in Source Code Form, including any
163 | Modifications that You create or to which You contribute, must be under
164 | the terms of this License. You must inform recipients that the Source
165 | Code Form of the Covered Software is governed by the terms of this
166 | License, and how they can obtain a copy of this License. You may not
167 | attempt to alter or restrict the recipients' rights in the Source Code
168 | Form.
169 |
170 | 3.2. Distribution of Executable Form
171 |
172 | If You distribute Covered Software in Executable Form then:
173 |
174 | (a) such Covered Software must also be made available in Source Code
175 | Form, as described in Section 3.1, and You must inform recipients of
176 | the Executable Form how they can obtain a copy of such Source Code
177 | Form by reasonable means in a timely manner, at a charge no more
178 | than the cost of distribution to the recipient; and
179 |
180 | (b) You may distribute such Executable Form under the terms of this
181 | License, or sublicense it under different terms, provided that the
182 | license for the Executable Form does not attempt to limit or alter
183 | the recipients' rights in the Source Code Form under this License.
184 |
185 | 3.3. Distribution of a Larger Work
186 |
187 | You may create and distribute a Larger Work under terms of Your choice,
188 | provided that You also comply with the requirements of this License for
189 | the Covered Software. If the Larger Work is a combination of Covered
190 | Software with a work governed by one or more Secondary Licenses, and the
191 | Covered Software is not Incompatible With Secondary Licenses, this
192 | License permits You to additionally distribute such Covered Software
193 | under the terms of such Secondary License(s), so that the recipient of
194 | the Larger Work may, at their option, further distribute the Covered
195 | Software under the terms of either this License or such Secondary
196 | License(s).
197 |
198 | 3.4. Notices
199 |
200 | You may not remove or alter the substance of any license notices
201 | (including copyright notices, patent notices, disclaimers of warranty,
202 | or limitations of liability) contained within the Source Code Form of
203 | the Covered Software, except that You may alter any license notices to
204 | the extent required to remedy known factual inaccuracies.
205 |
206 | 3.5. Application of Additional Terms
207 |
208 | You may choose to offer, and to charge a fee for, warranty, support,
209 | indemnity or liability obligations to one or more recipients of Covered
210 | Software. However, You may do so only on Your own behalf, and not on
211 | behalf of any Contributor. You must make it absolutely clear that any
212 | such warranty, support, indemnity, or liability obligation is offered by
213 | You alone, and You hereby agree to indemnify every Contributor for any
214 | liability incurred by such Contributor as a result of warranty, support,
215 | indemnity or liability terms You offer. You may include additional
216 | disclaimers of warranty and limitations of liability specific to any
217 | jurisdiction.
218 |
219 | 4. Inability to Comply Due to Statute or Regulation
220 | ---------------------------------------------------
221 |
222 | If it is impossible for You to comply with any of the terms of this
223 | License with respect to some or all of the Covered Software due to
224 | statute, judicial order, or regulation then You must: (a) comply with
225 | the terms of this License to the maximum extent possible; and (b)
226 | describe the limitations and the code they affect. Such description must
227 | be placed in a text file included with all distributions of the Covered
228 | Software under this License. Except to the extent prohibited by statute
229 | or regulation, such description must be sufficiently detailed for a
230 | recipient of ordinary skill to be able to understand it.
231 |
232 | 5. Termination
233 | --------------
234 |
235 | 5.1. The rights granted under this License will terminate automatically
236 | if You fail to comply with any of its terms. However, if You become
237 | compliant, then the rights granted under this License from a particular
238 | Contributor are reinstated (a) provisionally, unless and until such
239 | Contributor explicitly and finally terminates Your grants, and (b) on an
240 | ongoing basis, if such Contributor fails to notify You of the
241 | non-compliance by some reasonable means prior to 60 days after You have
242 | come back into compliance. Moreover, Your grants from a particular
243 | Contributor are reinstated on an ongoing basis if such Contributor
244 | notifies You of the non-compliance by some reasonable means, this is the
245 | first time You have received notice of non-compliance with this License
246 | from such Contributor, and You become compliant prior to 30 days after
247 | Your receipt of the notice.
248 |
249 | 5.2. If You initiate litigation against any entity by asserting a patent
250 | infringement claim (excluding declaratory judgment actions,
251 | counter-claims, and cross-claims) alleging that a Contributor Version
252 | directly or indirectly infringes any patent, then the rights granted to
253 | You by any and all Contributors for the Covered Software under Section
254 | 2.1 of this License shall terminate.
255 |
256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all
257 | end user license agreements (excluding distributors and resellers) which
258 | have been validly granted by You or Your distributors under this License
259 | prior to termination shall survive termination.
260 |
261 | ************************************************************************
262 | * *
263 | * 6. Disclaimer of Warranty *
264 | * ------------------------- *
265 | * *
266 | * Covered Software is provided under this License on an "as is" *
267 | * basis, without warranty of any kind, either expressed, implied, or *
268 | * statutory, including, without limitation, warranties that the *
269 | * Covered Software is free of defects, merchantable, fit for a *
270 | * particular purpose or non-infringing. The entire risk as to the *
271 | * quality and performance of the Covered Software is with You. *
272 | * Should any Covered Software prove defective in any respect, You *
273 | * (not any Contributor) assume the cost of any necessary servicing, *
274 | * repair, or correction. This disclaimer of warranty constitutes an *
275 | * essential part of this License. No use of any Covered Software is *
276 | * authorized under this License except under this disclaimer. *
277 | * *
278 | ************************************************************************
279 |
280 | ************************************************************************
281 | * *
282 | * 7. Limitation of Liability *
283 | * -------------------------- *
284 | * *
285 | * Under no circumstances and under no legal theory, whether tort *
286 | * (including negligence), contract, or otherwise, shall any *
287 | * Contributor, or anyone who distributes Covered Software as *
288 | * permitted above, be liable to You for any direct, indirect, *
289 | * special, incidental, or consequential damages of any character *
290 | * including, without limitation, damages for lost profits, loss of *
291 | * goodwill, work stoppage, computer failure or malfunction, or any *
292 | * and all other commercial damages or losses, even if such party *
293 | * shall have been informed of the possibility of such damages. This *
294 | * limitation of liability shall not apply to liability for death or *
295 | * personal injury resulting from such party's negligence to the *
296 | * extent applicable law prohibits such limitation. Some *
297 | * jurisdictions do not allow the exclusion or limitation of *
298 | * incidental or consequential damages, so this exclusion and *
299 | * limitation may not apply to You. *
300 | * *
301 | ************************************************************************
302 |
303 | 8. Litigation
304 | -------------
305 |
306 | Any litigation relating to this License may be brought only in the
307 | courts of a jurisdiction where the defendant maintains its principal
308 | place of business and such litigation shall be governed by laws of that
309 | jurisdiction, without reference to its conflict-of-law provisions.
310 | Nothing in this Section shall prevent a party's ability to bring
311 | cross-claims or counter-claims.
312 |
313 | 9. Miscellaneous
314 | ----------------
315 |
316 | This License represents the complete agreement concerning the subject
317 | matter hereof. If any provision of this License is held to be
318 | unenforceable, such provision shall be reformed only to the extent
319 | necessary to make it enforceable. Any law or regulation which provides
320 | that the language of a contract shall be construed against the drafter
321 | shall not be used to construe this License against a Contributor.
322 |
323 | 10. Versions of the License
324 | ---------------------------
325 |
326 | 10.1. New Versions
327 |
328 | Mozilla Foundation is the license steward. Except as provided in Section
329 | 10.3, no one other than the license steward has the right to modify or
330 | publish new versions of this License. Each version will be given a
331 | distinguishing version number.
332 |
333 | 10.2. Effect of New Versions
334 |
335 | You may distribute the Covered Software under the terms of the version
336 | of the License under which You originally received the Covered Software,
337 | or under the terms of any subsequent version published by the license
338 | steward.
339 |
340 | 10.3. Modified Versions
341 |
342 | If you create software not governed by this License, and you want to
343 | create a new license for such software, you may create and use a
344 | modified version of this License if you rename the license and remove
345 | any references to the name of the license steward (except to note that
346 | such modified license differs from this License).
347 |
348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary
349 | Licenses
350 |
351 | If You choose to distribute Source Code Form that is Incompatible With
352 | Secondary Licenses under the terms of this version of the License, the
353 | notice described in Exhibit B of this License must be attached.
354 |
355 | Exhibit A - Source Code Form License Notice
356 | -------------------------------------------
357 |
358 | This Source Code Form is subject to the terms of the Mozilla Public
359 | License, v. 2.0. If a copy of the MPL was not distributed with this
360 | file, You can obtain one at http://mozilla.org/MPL/2.0/.
361 |
362 | If it is not possible or desirable to put the notice in a particular
363 | file, then You may include the notice in a location (such as a LICENSE
364 | file in a relevant directory) where a recipient would be likely to look
365 | for such a notice.
366 |
367 | You may add additional accurate notices of copyright ownership.
368 |
369 | Exhibit B - "Incompatible With Secondary Licenses" Notice
370 | ---------------------------------------------------------
371 |
372 | This Source Code Form is "Incompatible With Secondary Licenses", as
373 | defined by the Mozilla Public License, v. 2.0.
374 |
--------------------------------------------------------------------------------
/bootstrap.yml:
--------------------------------------------------------------------------------
1 | version: '3.5'
2 | name: sandcastles
3 |
4 | services:
5 | root-ca:
6 | image: smallstep/step-ca:0.27.1
7 | environment:
8 | - DOCKER_STEPCA_INIT_NAME=Letterbook Sandcastles
9 | - DOCKER_STEPCA_INIT_DNS_NAMES=root-ca.castle,root-ca,localhost
10 | - DOCKER_STEPCA_INIT_REMOTE_MANAGEMENT=true
11 | - DOCKER_STEPCA_INIT_ACME=true
12 | - DOCKER_STEPCA_INIT_PASSWORD=capassword
13 | ports:
14 | - "9000:9000"
15 | networks:
16 | default:
17 | restart: unless-stopped
--------------------------------------------------------------------------------
/castle:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 |
3 | # shellcheck disable=SC3024
4 | # shellcheck disable=SC3030
5 | # shellcheck disable=SC1078
6 | DIR=$(dirname "$(readlink -f -- "$0")")
7 | C_ERROR="\033[0;31m"
8 | C_WARN="\033[0;33m"
9 | C_VERBOSE="\033[0;32m"
10 | C_NOTICE="\033[0;34m"
11 | C_NONE="\033[0m"
12 | set_usage() {
13 | USAGE="""Usage: ${CMD:=${0##*/}} ${1:-"[bootstrap|up|down|build|help]"} [APPS...|(-a|--all)] [(-v|--verbose)] [(-d|--dry-run)] [(-h|--help)]
14 | APPS:
15 | The apps to run this command on.
16 |
17 | Options:
18 | -a|--all - apply to all apps
19 | -v|--verbose - output additional info to stderr; pass multiple times for greater verbosity
20 | -d|--dry-run - preview the commends to be run, but do not execute them
21 | -h|--help - display help text
22 | ${2}
23 |
24 | Examples:
25 | ${C_VERBOSE}Bootstrap the project${C_NONE}
26 | ./castle bootstrap
27 |
28 | ${C_VERBOSE}Build baseline required images, plus mastodon and sharkey${C_NONE}
29 | ./castle build mastodon sharkey --required
30 |
31 | ${C_VERBOSE}Run mastodon and letterbook${C_NONE}
32 | ./castle up mastodon letterbook"""
33 | }; set_usage;
34 | set_help() {
35 | HELP="""Sandcastles - ${C_VERBOSE}${1:-"federation sandbox"}${C_NONE}
36 | ${2:-"""Easily and quickly set up a federation sandbox, for testing against
37 | production-like configurations of multiple fediverse backends."""}
38 | """
39 | }; set_help;
40 | exit2 () { printf >&2 "%s: %s: '%s'\n%b\n" "$CMD" "$1" "$2" "$USAGE"; exit 2; }
41 | exit_runtime () {
42 | printf >&2 "Sandcastles require a container runtime\nYou must install either podman (and podman-compose) or docker";
43 | exit 3;
44 | }
45 | exit_err() {
46 | printf >&2 "${C_ERROR}%s Error!${C_NONE}\n%b\n" "$1" "⤷ $2"; exit 4
47 | }
48 | exit_usage() { printf "%b\n" "$USAGE"; exit 0; }
49 | exit_help() { printf "%b\n%b\n" "$HELP" "$USAGE"; exit 0; }
50 | check () { { [ "$1" != "$EOL" ] && [ "$1" != '--' ]; } || exit2 "missing argument" "$2"; } # avoid infinite loop
51 | verbose () {
52 | if [ "$opt_verbose" -gt 0 ]; then
53 | debug=$1; shift
54 | printf "${C_VERBOSE}%s${C_NONE}\n%s\n" "$debug" "⤷ $(printf "%s " "$@")" >&2
55 | fi
56 | }
57 | verbose2 () {
58 | if [ $opt_verbose -gt 1 ]; then
59 | verbose "$@"
60 | fi
61 | }
62 |
63 | # run commands with support for verbose output and dry-runs
64 | run() {
65 | debug=$1; shift
66 | cmd_args=("$@")
67 | if [ "$opt_dryrun" = "true" ]; then
68 | printf "${C_NOTICE}(dry-run) ${C_VERBOSE}$debug${C_NONE}\n%s\n" "⤷ $(printf "%s %s" "${cmd_args[*]}" "${arg_pass[*]}")" >&2
69 | return
70 | fi
71 | if [ "$opt_verbose" -gt 0 ]; then
72 | verbose "$debug" "${cmd_args[*]}"
73 | fi
74 | if [ ${#arg_pass[@]} -gt 0 ]; then
75 | verbose "passthrough" "${#arg_pass[@]}"
76 | "${cmd_args[@]}" "${arg_pass[*]}"
77 | else
78 | "${cmd_args[@]}"
79 | fi
80 | code=$?
81 | if [ $code = 0 ]; then
82 | return
83 | fi
84 | exit $code
85 | }
86 |
87 | # parse action
88 | case "$1" in
89 | # handle help cases
90 | -h | --help | help ) exit_help;;
91 | '' ) exit_usage;;
92 | bootstrap ) opt_action=$1; shift
93 | set_help "$opt_action" "perform first time setup"
94 | set_usage "$opt_action"
95 | ;;
96 | up ) opt_action=$1; shift
97 | set_help "$opt_action" "run the selected apps"
98 | set_usage "$opt_action"
99 | ;;
100 | down ) opt_action=$1; shift
101 | set_help "$opt_action" "shut down the selected apps"
102 | set_usage "$opt_action"
103 | ;;
104 | build ) opt_action=$1; shift
105 | set_help "$opt_action" "Build container images with trust for your Sandcastle private CA"
106 | set_usage "$opt_action" "-r|--required - build required container images not associated with an individual app"
107 | ;;
108 | new ) opt_action=$1; shift
109 | set_help "$opt_action" "Scaffold up required files to add new apps to the Sandcastle. New app names should follow the command"
110 | ;;
111 | * ) exit2 "invalid command" "$opt_action";;
112 | esac
113 |
114 | # parse remaining command-line options
115 | set -- "$@" "${EOL:=$(printf '\1\3\3\7')}" # end-of-list marker
116 | opt_apps=()
117 | opt_verbose=0
118 | arg_pass=()
119 | while [ "$1" != "$EOL" ]; do
120 | opt="$1"; shift
121 | case "$opt" in
122 |
123 | # defined options
124 | -a | --all ) opt_all=true;;
125 | -h | --help ) exit_help;;
126 | -r | --required ) opt_required=true;;
127 | -v | --verbose ) opt_verbose=$((opt_verbose + 1));;
128 | -d | --dry-run ) opt_dryrun=true;;
129 |
130 | # process special cases
131 | --) while [ "$1" != "$EOL" ]; do set -- "$@" "$1"; arg_pass+=("$1"); shift; done;; # parse remaining as passthrough
132 | --[!=]*=*) set -- "${opt%%=*}" "${opt#*=}" "$@";; # "--opt=arg" -> "--opt" "arg"
133 | -[A-Za-z0-9] | -*[!A-Za-z0-9]*) exit2 "invalid option" "$opt";; # anything invalid like '-*'
134 | -?*) other="${opt#-?}"; set -- "${opt%$other}" "-${other}" "$@";; # "-abc" -> "-a" "-bc"
135 | *) opt_apps+=("$opt");; # positional, rotate to the end
136 | esac
137 | done; shift
138 |
139 | if (command -v podman >/dev/null 2>&1) && (command -v podman-compose >/dev/null 2>&1); then
140 | env_runtime="podman"
141 | elif command -v docker >/dev/null 2>&1; then
142 | env_runtime="docker"
143 | else
144 | exit_runtime
145 | fi
146 |
147 | verbose2 "parsed options" """DIR = '$DIR'
148 | action = '$opt_action'
149 | arg_pass = ${arg_pass[*]}
150 | apps = ${opt_apps[*]}
151 | --all = $opt_all
152 | --required = $opt_required
153 | --verbose = $opt_verbose
154 | --dry-run = $opt_dryrun
155 | runtime = '$env_runtime'"""
156 |
157 | # Zhu Li, do the thing!
158 | case $opt_action in
159 | up )
160 | compose_files=("-f" "$DIR/docker-compose.yml")
161 | if [ "$opt_all" = "true" ]; then
162 | all_files=$(find "$DIR" -type f -name "*.compose.yml")
163 | for n in ${all_files[*]}; do compose_files+=("-f" "$n"); done
164 | else
165 | for n in ${opt_apps[*]}; do compose_files+=("-f" "$n.compose.yml"); done
166 | fi
167 | cmd_args=("$env_runtime" "compose" "${compose_files[@]}" "up" "-d")
168 | if [ "$env_runtime" = "podman" ]; then
169 | CONTAINERS_CONF_OVERRIDE="$DIR/volumes/containers.conf"
170 | export CONTAINERS_CONF_OVERRIDE
171 | fi
172 | run "compose up" "${cmd_args[@]}"
173 | ;;
174 |
175 | build )
176 | docker_files=()
177 | if [ "$opt_all" = "true" ]; then
178 | verbose "find all Dockerfiles"
179 | docker_files+=$(find "$DIR" -type f -name "*.Dockerfile")
180 | elif [ "$opt_required" = "true" ]; then
181 | verbose "find Dockerfiles" "--required"
182 | docker_files+=("$DIR/traefik.Dockerfile" "$DIR/network-tools.Dockerfile")
183 | fi
184 | if [ "$opt_all" != "true" ]; then
185 | for n in ${opt_apps[*]}; do
186 | verbose "find Dockerfiles" "$n"
187 | found=$(find "$DIR" -type f -name "$n*.Dockerfile")
188 | docker_files+=("$found")
189 | done
190 | fi
191 | verbose "found" ${docker_files[*]}
192 |
193 | # tags() is an array of "tuples" which don't exist in sh scripts as far as I know.
194 | # so, it's actually a regular array, but with paired elements.
195 | # Like ("x.Dockerfile" "tag/x:latest" "y.Dockerfile" "tag/y:latest")...
196 | tags=()
197 | for n in ${docker_files[*]}; do
198 | tags+=("$n" "localhost/sandcastles/$(basename ${n//.Dockerfile})");
199 | done
200 |
201 | i=0
202 | while [ "$i" -lt ${#tags[@]} ]; do
203 | cmd_args=("$env_runtime" "build" "$DIR" "-f" "${tags[i]}" "-t" "${tags[i+1]}:latest")
204 | if [ $env_runtime = "podman" ]; then
205 | cmd_args+=("--format" "docker")
206 | fi
207 | run "build ${tags[i+1]}" "${cmd_args[@]}"
208 | i=$((i+2))
209 | done
210 | ;;
211 |
212 | down )
213 | compose_files=()
214 | if [ "$opt_all" = "true" ]; then
215 | compose_files+=("-f" "$DIR/docker-compose.yml")
216 | all_files=$(find "$DIR" -type f -name "*.compose.yml")
217 | for n in ${all_files[*]}; do compose_files+=("-f" "$n"); done
218 | else
219 | for n in ${opt_apps[*]}; do compose_files+=("-f" "$n.compose.yml"); done
220 | fi
221 | cmd_args=("$env_runtime" "compose" "${compose_files[@]}" "down")
222 | run "compose down" "${cmd_args[@]}"
223 | ;;
224 |
225 | bootstrap )
226 | if [ -d "$DIR/volumes/root-ca/secrets" ]; then
227 | exit_err "$opt_action" """Refusing to overwrite data in ${C_WARN}$DIR/volumes/root-ca/${C_NONE}
228 | This project has already been bootstrapped. You must remove the secrets stored in
229 | ${C_WARN}$DIR/volumes/root-ca/${C_NONE} before you can bootstrap again.
230 | Those secrets cannot be recovered later! Make a copy of them, or revoke trust in them before they are deleted."""
231 | fi
232 |
233 | run "create sandcastles internal CA" "$env_runtime" "compose" "-f" "$DIR/bootstrap.yml" "up" "-d"
234 | container_id=$(run "get container id" "$env_runtime" "compose" "-f" "bootstrap.yml" "ps" "-q")
235 | container_id=${container_id:-"dry-run_container"}
236 | run "wait for container" sleep 1
237 | run "extract private key for sandcastles internal CA" \
238 | "$env_runtime" "cp" "$container_id:/home/step/templates" "$DIR/volumes/root-ca/"
239 | run "..." "$env_runtime" "cp" "$container_id:/home/step/secrets" "$DIR/volumes/root-ca/"
240 | run "..." "$env_runtime" "cp" "$container_id:/home/step/db" "$DIR/volumes/root-ca/"
241 | run "..." "$env_runtime" "cp" "$container_id:/home/step/certs" "$DIR/volumes/root-ca/"
242 | run "..." "$env_runtime" "compose" "-f" "bootstrap.yml" "down"
243 |
244 | run "configure access to sandcastles internal CA" mkdir "-p" "$DIR/volumes/root-ca/config"
245 | run "..." "cp" "$DIR/volumes/ca.json" "$DIR/volumes/root-ca/config/ca.json"
246 | run "..." "find" "$DIR/volumes/root-ca" "-type" "d" "-exec" "chmod" "755" "{}" "+"
247 | run "..." "find" "$DIR/volumes/root-ca" "-type" "f" "-exec" "chmod" "644" "{}" "+"
248 |
249 | run "collect info about the runtime environment" \
250 | printf "%s\n" "DOCKER_PATH=${DOCKER_HOST//"unix://"}" > "$DIR/.env"
251 | run "..." printf "%s\n" """[containers]
252 | base_hosts_file = \"$DIR/volumes/hosts\" """ > "$DIR/volumes/containers.conf"
253 | ;;
254 |
255 | new )
256 | compose_template=$(cat "$DIR/templates/compose.yml")
257 | docker_template=$(cat "$DIR/templates/Dockerfile")
258 | for n in ${opt_apps[*]}; do
259 | compose_file=${compose_template//"{{app_name}}"/$n}
260 | docker_file=${docker_template//"{{app_name}}"/$n}
261 | run "generate compose template for $n" printf "%s\n" "$compose_file" > "$DIR/$n.compose.yml"
262 | run "generate Dockerfile template for $n" printf "%s\n" "$docker_file" > "$DIR/$n.Dockerfile"
263 | done
264 | ;;
265 | esac
266 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.10'
2 | name: sandcastles
3 |
4 | services:
5 | network-tools:
6 | networks:
7 | default:
8 | image: localhost/sandcastles/network-tools:latest
9 | healthcheck:
10 | test: curl -sS --fail-with-body https://proxy.castle > /dev/null
11 | command: curl -Ivv https://proxy.castle
12 |
13 | proxy:
14 | image: localhost/sandcastles/traefik:latest
15 | build:
16 | dockerfile: proxy.Dockerfile
17 | tags:
18 | - localhost/traefik-sandcastle:latest
19 | security_opt:
20 | - label=type:container_runtime_t
21 | volumes:
22 | - '${DOCKER_PATH}:/var/run/docker.sock:z'
23 | - './volumes/proxy:/etc/traefik/:z'
24 | networks:
25 | default:
26 | aliases:
27 | - 'proxy.castle'
28 | ports:
29 | - '8080:80'
30 | - '8443:443'
31 | restart: unless-stopped
32 |
33 | root-ca:
34 | image: docker.io/smallstep/step-ca:0.27.1
35 | volumes:
36 | - './volumes/root-ca:/home/step:z'
37 | environment:
38 | - DOCKER_STEPCA_INIT_NAME=Letterbook Sandcastles
39 | - DOCKER_STEPCA_INIT_DNS_NAMES=root-ca.castle,root-ca,localhost
40 | - DOCKER_STEPCA_INIT_REMOTE_MANAGEMENT=true
41 | - DOCKER_STEPCA_INIT_ACME=false
42 | - DOCKER_STEPCA_INIT_PASSWORD=capassword
43 | ports:
44 | - "9000:9000"
45 | networks:
46 | default:
47 | aliases:
48 | - root-ca.castle
49 | restart: unless-stopped
50 |
51 | httpbin:
52 | scale: 0
53 | labels:
54 | - traefik.http.routers.httpbin.rule=Host(`httpbin.castle`)
55 | - traefik.http.routers.httpbin.tls=true
56 | - traefik.http.routers.httpbin.tls.certresolver=smallstep
57 | - traefik.port=80
58 | - traefik.enable=true
59 | image: docker.io/kennethreitz/httpbin
60 | networks:
61 | default:
62 | restart: unless-stopped
63 |
64 | networks:
65 | default:
66 |
--------------------------------------------------------------------------------
/letterbook.Dockerfile:
--------------------------------------------------------------------------------
1 | FROM docker.io/smallstep/step-cli:0.25.0 AS step-cli
2 |
3 | FROM mcr.microsoft.com/dotnet/sdk:8.0
4 |
5 | USER root
6 |
7 | COPY --from=step-cli /usr/local/bin/step /usr/local/bin/step
8 | COPY volumes/root-ca/certs/root_ca.crt /usr/local/share/ca-certificates/root_ca.crt
9 | RUN /usr/local/bin/step certificate install /usr/local/share/ca-certificates/root_ca.crt
10 |
11 | WORKDIR /app
12 |
--------------------------------------------------------------------------------
/letterbook.compose.yml:
--------------------------------------------------------------------------------
1 | # must be used as an overlay for the main docker-compose file. Ex
2 | # docker-compose -f docker-compose.yml -f letterbook.compose.yml up
3 |
4 | ### NOTICE ##
5 | #
6 | # Letterbook is not a typical sandcastle component, because it doesn't have any stable releases, yet. It's included here
7 | # as a development convenience, to make it easier to build and run the app from source, in the sandcastle environment.
8 | #
9 | # To do so, you must have a local clone of the source code, you must add it to a local `.env` file
10 | # as the LETTERBOOK_REPO var. Ex:
11 | # LETTERBOOK_REPO=/path/to/letterbook
12 |
13 | # defaults
14 | # handle: admin
15 | # login: admin@letterbook.example
16 | # password: Password1!
17 |
18 | services:
19 | letterbook:
20 | labels:
21 | - traefik.http.routers.letterbook.rule=Host(`letterbook.castle`)
22 | - traefik.http.routers.letterbook.tls=true
23 | - traefik.http.routers.letterbook.tls.certresolver=smallstep
24 | - traefik.http.services.letterbook-sandcastles.loadBalancer.healthCheck.path=/healthz
25 | - traefik.port=5127
26 | - traefik.enable=true
27 | - traefik.docker.network=sandcastles_letterbook
28 | image: localhost/sandcastles/letterbook:latest
29 | build:
30 | dockerfile: ./letterbook.Dockerfile
31 | tags:
32 | - localhost/sandcastles/letterbook:latest
33 | command: dotnet run --project Source/Letterbook/Letterbook.csproj -c Debug --launch-profile sandcastle
34 | environment:
35 | ASPNETCORE_ENVIRONMENT: Sandcastle
36 | OTEL_EXPORTER_OTLP_ENDPOINT: 'http://tempo:4317'
37 | volumes:
38 | - 'letterbook_nuget_cache:/root/.nuget/packages/'
39 | - 'letterbook_build_cache:/app/artifacts/'
40 | - '${LETTERBOOK_REPO}Source:/app/Source:z'
41 | - '${LETTERBOOK_REPO}Letterbook.sln:/app/Letterbook.sln:z'
42 | - '${LETTERBOOK_REPO}Directory.Build.props:/app/Directory.Build.props:z'
43 | - '${LETTERBOOK_REPO}Directory.Packages.props:/app/Directory.Packages.props:z'
44 | ports:
45 | - '2982:5127'
46 | - '5127:5127'
47 | networks:
48 | - default
49 | - letterbook
50 | healthcheck:
51 | test: curl -sS --fail-with-body localhost:5127/healthz
52 | interval: 2s
53 | timeout: 1s
54 | retries: 10
55 | depends_on:
56 | letterbook_db:
57 | condition: service_healthy
58 |
59 | letterbook_db:
60 | image: timescale/timescaledb:2.17.2-pg15-oss
61 | environment:
62 | - POSTGRES_USER=letterbook
63 | - POSTGRES_PASSWORD=letterbookpw
64 | - POSTGRES_DB=letterbook
65 | volumes:
66 | - letterbook_db_data:/var/lib/postgresql/data
67 | networks:
68 | - letterbook
69 | healthcheck:
70 | test: pg_isready -d letterbook -U postgres
71 | interval: 2s
72 | timeout: 1s
73 | retries: 10
74 | restart: always
75 |
76 |
77 |
78 | proxy:
79 | networks:
80 | default:
81 | aliases:
82 | - letterbook.castle
83 | letterbook: {}
84 |
85 | networks:
86 | letterbook:
87 | default:
88 |
89 | volumes:
90 | letterbook_db_data:
91 | letterbook_nuget_cache:
92 | letterbook_build_cache:
93 |
--------------------------------------------------------------------------------
/mastodon-streaming.Dockerfile:
--------------------------------------------------------------------------------
1 | FROM docker.io/smallstep/step-cli:0.25.0 AS step-cli
2 |
3 | FROM ghcr.io/mastodon/mastodon-streaming:v4.3.4
4 |
5 | USER root
6 |
7 | COPY --from=step-cli /usr/local/bin/step /usr/local/bin/step
8 | COPY volumes/root-ca/certs/root_ca.crt /usr/local/share/ca-certificates/root_ca.crt
9 | RUN /usr/local/bin/step certificate install /usr/local/share/ca-certificates/root_ca.crt
10 |
11 | USER mastodon
12 |
--------------------------------------------------------------------------------
/mastodon.Dockerfile:
--------------------------------------------------------------------------------
1 | FROM docker.io/smallstep/step-cli:0.25.0 AS step-cli
2 |
3 | FROM ghcr.io/mastodon/mastodon:v4.3.4
4 |
5 | USER root
6 |
7 | COPY --from=step-cli /usr/local/bin/step /usr/local/bin/step
8 | COPY volumes/root-ca/certs/root_ca.crt /usr/local/share/ca-certificates/root_ca.crt
9 | RUN /usr/local/bin/step certificate install /usr/local/share/ca-certificates/root_ca.crt
10 |
11 | USER mastodon
12 |
--------------------------------------------------------------------------------
/mastodon.compose.yml:
--------------------------------------------------------------------------------
1 | # must be used as an overlay for the main docker-compose file. Ex
2 | # docker-compose -f docker-compose.yml -f mastodon.castle.yml up
3 |
4 | # defaults
5 | # handle: castle_mastodon
6 | # admin: castle@mastodon.castle
7 | # password: password
8 |
9 | services:
10 | mastodon:
11 | labels:
12 | - traefik.http.routers.mastodon.rule=Host(`mastodon.castle`)
13 | - traefik.http.routers.mastodon.tls=true
14 | - traefik.http.routers.mastodon.tls.certresolver=smallstep
15 | - traefik.http.services.mastodon-sandcastles.loadBalancer.healthCheck.path=/health
16 | - traefik.port=2970
17 | - traefik.enable=true
18 | - traefik.docker.network=sandcastles_mastodon
19 | depends_on:
20 | mastodon_db:
21 | condition: service_started
22 | mastodon_redis:
23 | condition: service_started
24 | mastodon-init:
25 | condition: service_completed_successfully
26 | image: localhost/sandcastles/mastodon:latest
27 | build:
28 | dockerfile: ./mastodon.Dockerfile
29 | target: mastodon
30 | tags:
31 | - localhost/sandcastles/mastodon:latest
32 | command: bundle exec rails server -p 2970 -b 0.0.0.0
33 | # command: bundle exec puma -C config/puma.rb
34 | # command: tail -f /dev/null
35 | volumes: &mastodon_data_web_volume
36 | - 'mastodon_data_web:/mastodon/public/system:z'
37 | ports:
38 | - '2970:2970'
39 | networks:
40 | default:
41 | mastodon:
42 | environment: &mastodon_env
43 | LOCAL_DOMAIN: mastodon.castle
44 | ALTERNATE_DOMAINS: 127.0.0.1:2970,localhost:2970
45 | DB_HOST: mastodon_db
46 | DB_USER: mastodon
47 | DB_NAME: mastodon
48 | DB_PASS: password
49 | DB_PORT: 5432
50 | REDIS_HOST: mastodon_redis
51 | SECRET_KEY_BASE: 6f8fbd95f1e6b3d15121c6d0c54cae5efebeec70cf4d5c9ee09158a8241ffcb01a9d13b66e95ef8bc5adfc50c20ce9ffb3659b7c09e94449a45dd28582f5a578
52 | OTP_SECRET: 6163a17e06524facdb86fccb0293b6f6070cc6a493cba9d663451a477503896d3ac3dd300193c4773b5eefe97da22564e6b2a5555b43ced647e35e930d3687ed
53 | S3_ENABLED: 'false'
54 | ES_ENABLED: 'false'
55 | HTTPS_ENABLED: false
56 | LOCAL_HTTPS: false
57 | ALLOWED_PRIVATE_ADDRESSES: 10.0.0.0/8,127.0.0.1,172.0.0.0/8,192.0.0.0/8
58 | RAILS_ENV: production
59 | RAILS_LOG_LEVEL: info
60 | ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY: fkSxKD2bF396kdQbrP1EJ7WbU7ZgNokR
61 | ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT: r0hvVmzBVsjxC7AMlwhOzmtc36ZCOS1E
62 | ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY: PhdFyyfy5xJ7WVd2lWBpcPScRQHzRTNr
63 | # restart: on-failure
64 | healthcheck:
65 | test: ['CMD-SHELL',"curl -s --noproxy localhost localhost:2970/health | grep -q 'OK' || exit 1"]
66 | interval: 5s
67 | timeout: 1s
68 | retries: 10
69 |
70 | mastodon-init:
71 | image: ghcr.io/mastodon/mastodon:v4.3.4
72 | command: /opt/mastodon/init.sh
73 | volumes:
74 | - './volumes/mastodon/init.sh:/opt/mastodon/init.sh:ro'
75 | - 'mastodon_data_web:/mastodon/public/system:z'
76 | networks:
77 | - mastodon
78 | environment:
79 | <<: *mastodon_env
80 | RAILS_ENVIRONMENT: production
81 | FORCE_INIT: 'false'
82 |
83 | mastodon-streaming:
84 | scale: 0
85 | depends_on:
86 | mastodon:
87 | condition: service_healthy
88 | build:
89 | dockerfile: mastodon.Dockerfile
90 | target: mastodon-streaming
91 | tags:
92 | - localhost/sandcastles/mastodon:latest
93 | image: localhost/sandcastles/mastodon-streaming:latest
94 | command: node ./streaming/index.js
95 | networks:
96 | - mastodon
97 | environment:
98 | <<: *mastodon_env
99 | MASTODON_MODE: streaming
100 | restart: on-failure
101 |
102 | mastodon-sidekiq:
103 | depends_on:
104 | mastodon:
105 | condition: service_healthy
106 | image: localhost/sandcastles/mastodon:latest
107 | build:
108 | dockerfile: mastodon.Dockerfile
109 | target: mastodon
110 | tags:
111 | - localhost/sandcastles/mastodon:latest
112 | command: bundle exec sidekiq
113 | volumes: *mastodon_data_web_volume
114 | networks:
115 | - default
116 | - mastodon
117 | environment:
118 | <<: *mastodon_env
119 | MASTODON_MODE: sidekiq
120 | restart: on-failure
121 |
122 | mastodon_db:
123 | image: docker.io/postgres:16-alpine
124 | command:
125 | - postgres
126 | - -c
127 | - config_file=/etc/postgresql/postgresql.conf
128 | environment:
129 | POSTGRES_DB: mastodon
130 | POSTGRES_PASSWORD: password
131 | POSTGRES_USER: mastodon
132 | POSTGRES_HOST_AUTH_METHOD: scram-sha-256
133 | networks:
134 | - mastodon
135 | volumes:
136 | - mastodon_data_db:/var/lib/postgresql/data
137 | - ./volumes/postgresql.conf:/etc/postgresql/postgresql.conf:z
138 |
139 | mastodon_redis:
140 | image: docker.io/redis:7.4-alpine
141 | environment:
142 | - ALLOW_EMPTY_PASSWORD=yes
143 | volumes:
144 | - 'mastodon_data_redis:/data'
145 | networks:
146 | - mastodon
147 | restart: 'no'
148 |
149 | proxy:
150 | networks:
151 | default:
152 | aliases:
153 | - mastodon.castle
154 | mastodon: {}
155 |
156 | volumes:
157 | mastodon_data_db:
158 | driver: local
159 | mastodon_data_redis:
160 | driver: local
161 | mastodon_data_web:
162 | driver: local
163 |
164 | networks:
165 | mastodon:
166 | default:
167 |
--------------------------------------------------------------------------------
/network-tools.Dockerfile:
--------------------------------------------------------------------------------
1 | FROM docker.io/smallstep/step-cli:0.25.0 AS step-cli
2 |
3 | FROM docker.io/jonlabelle/network-tools:latest
4 |
5 | USER root
6 |
7 | COPY --from=step-cli /usr/local/bin/step /usr/local/bin/step
8 | COPY volumes/root-ca/certs/root_ca.crt /usr/local/share/ca-certificates/root_ca.crt
9 | RUN /usr/local/bin/step certificate install /usr/local/share/ca-certificates/root_ca.crt
10 |
--------------------------------------------------------------------------------
/pasture.Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.11-alpine
2 |
3 | RUN pip install ipython
4 | RUN pip install bovine
5 |
6 | RUN pip install fediverse_pasture
7 |
8 | ADD https://codeberg.org/helge/funfedidev/archive/cd14bd038b3733f8efa343c2157dfd8768e641f9.zip /var/source.zip
9 |
10 | RUN unzip /var/source.zip -d /var/source/
11 | RUN mkdir /work
12 | RUN cp /var/source/funfedidev/fediverse-pasture/work / -r
13 | WORKDIR /opt
14 |
15 | COPY volumes/root-ca/certs/root_ca.crt /usr/local/share/ca-certificates/root_ca.crt
16 |
17 | RUN cat /usr/local/share/ca-certificates/root_ca.crt >> /etc/ssl/certs/ca-certificates.crt
--------------------------------------------------------------------------------
/pasture.castle.yml:
--------------------------------------------------------------------------------
1 |
2 | services:
3 | pasture_one_actor:
4 | image: pasture-sandcastles
5 | build:
6 | dockerfile: ./pasture.Dockerfile
7 | volumes:
8 | - ./volumes/pasture:/opt
9 | command: python -m fediverse_pasture.one_actor --port 80 --assets assets
10 | networks:
11 | pasture:
12 | pasture_runner:
13 | image: pasture-sandcastles
14 | build:
15 | dockerfile: ./pasture.Dockerfile
16 | volumes:
17 | - ./volumes/pasture:/opt
18 | working_dir: /work
19 | depends_on:
20 | - pasture_one_actor
21 | command: /bin/sh
22 | stdin_open: true
23 | tty: true
24 | networks:
25 | pasture:
26 | pasture_http_signature:
27 | image: pasture-sandcastles
28 | build:
29 | dockerfile: ./pasture.Dockerfile
30 | volumes:
31 | - ./volumes/pasture:/opt
32 | command: python -m fediverse_pasture.http_signature --port 80
33 | networks:
34 | pasture:
35 | pasture_verify_actor:
36 | container_name: actor.pasture.castle
37 | labels:
38 | - traefik.http.routers.pasture.rule=Host(`actor.pasture.castle`)
39 | - traefik.http.routers.pasture.tls=true
40 | - traefik.http.routers.pasture.tls.certresolver=smallstep
41 | - traefik.port=80
42 | - traefik.enable=true
43 | image: pasture-sandcastles
44 | build:
45 | dockerfile: ./pasture.Dockerfile
46 | volumes:
47 | - ./volumes/pasture:/opt
48 | command: python -m fediverse_pasture.verify_actor --port 80 --domain pasture_verify_actor
49 | expose:
50 | - 80
51 | networks:
52 | pasture:
53 | fediverse:
54 |
55 | proxy:
56 | networks:
57 | default:
58 | aliases:
59 | - actor.pasture.castle
60 | fediverse:
61 | aliases:
62 | - actor.pasture.castle
63 |
64 |
65 | networks:
66 | pasture:
67 | internal: true
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Sandcastles
2 |
3 | > Heavenly, she's so heavenly
4 | > When she smiles at you and she helps you build
5 | > Castles in the sand
6 |
7 | The Letterbook Sandcastles project offers an integration and federation test sandbox for developers of fediverse software. The goal is to make it easy to set up local instances of most fediverse servers, which can all federate with each other, with minimal necessary configuration. The whole environment is meant to be entirely local and self-contained. It doesn't require you to buy a domain name, or host any services exposed to the internet. You can also run your own under-development software without much difficulty, as long as you can run it in a docker container.
8 |
9 | # How it Works
10 | This is accomplished by running them all in a docker compose project, along with some supporting infrastructure to provision and use SSL certificates.
11 |
12 | ## Smallstep Certificate Authority
13 | This provides a root certificate authority which can issue SSL certificates to all of the other servers managed by the project. These servers are preconfigured to trust this CA, and the certificates will be provisioned as needed.
14 |
15 | ## Traefik Reverse Proxy
16 | Traefik serves as a reverse proxy, handling *all* of the federated traffick between services. It does this so that it can also manage their SSL certificates and connections. Traefik will automatically request or renew the certificates from Smallstep.
17 |
18 | # Available Services
19 |
20 | - [x] Mastodon (v4.3.4)
21 | - [x] Letterbook (development source build)
22 | - [ ] GoToSocial (#9)
23 | - [ ] Sharkey (#8)
24 | - [ ] Akkoma
25 | - [ ] Iceshrimp.NET
26 | - [ ] NodeBB
27 |
28 | # Getting Started
29 |
30 | ## Prerequisites
31 |
32 | You will need a docker run time and a docker client that supports docker compose. The easiest way to do that is to just install docker desktop. It's also confirmed to work with (rootless) podman compose. If you want to use a rootless container runtime, podman seems to work better than docker.
33 |
34 | You may also want to [install the `step cli`](https://smallstep.com/docs/step-cli/installation/). This isn't strictly necessary, but it will make it a lot easier to manage your certificates, and to add your new internal root CA as a trusted CA on your local computer.
35 |
36 | ## Quickstart
37 |
38 | If you just want to get up and running as quickly as possible, this is the process:
39 |
40 | ```shell
41 | git clone https://github.com/Letterbook/Sandcastles.git
42 | cd Sandcastles
43 | ./castle bootstrap
44 | ./castle build --all
45 | ./castle up --all
46 | # At this point, all of the available services should be running. Or, at
47 | # least initializing, and they'll be up soon. You can interact with them
48 | # and exchange federated messages, check their logs, and do any other
49 | # testing you want.
50 | ```
51 |
52 | And when you're done, shut it all down:
53 | ```shell
54 | ./castle down --all
55 | ```
56 |
57 | ## Steps
58 |
59 | Read on for a more detailed discussion of using sandcastles, what the scripts are doing, and how it works.
60 |
61 | ### 1. Clone this repo
62 | ```shell
63 | git clone https://github.com/Letterbook/Sandcastles.git
64 | cd Sandcastles
65 | ```
66 |
67 | ### 2. Bootstrap and run the project
68 |
69 | You can use the provided `castle` script to perform most actions with the project. It _should_ work with most common shells, but was made in `bash`. It also _should_ work in linux, mac, and windows (under WSL). But, it was made in a linux environment. If you want to inspect what `castle` will do before you execute it, there is a `--dry-run` flag you can use for that.
70 |
71 | One of the core functions of the Sandcastles project is to automatically provision trusted TLS certificates and https endpoints for all the fediverse services the project manages. To accomplish this offline, we need to run our own internal certificate authority, so that we can issue certificates to our services for their internal hostnames. Bootstrapping initializes that internal CA, and makes it's root certificate available so other services can be set up to trust it. The `bootstrap` command normally only needs to be run once, unless you need to regenerate your private keys for the internal sandcastles CA.
72 | ```shell
73 | ./castle bootstrap
74 | ```
75 |
76 | > [!Tip]
77 | > For all commands other than bootstrap, you can specify individual services to manage, or use the `--all` flag to cover all of them.
78 |
79 | We have to build our own container images so that we can add our own trusted certificate authority. In most cases, that's the only modification we need to make to the container images that projects provide. You should build the container images before first use. This is necessary if you're running on `podman`. If you're using `docker`, it's still a good idea. You may need to rebuild your container images periodically, to receive updates or test your changes.
80 | ```shell
81 | ./castle build --all
82 | ```
83 |
84 | Then you can run and interact with the apps.
85 | ```shell
86 | ./castle up mastodon
87 | # do stuff
88 | ./castle down --all
89 | ```
90 |
91 | Check the help for more details.
92 |
93 | ```shell
94 | ./castle help
95 | ./castle up --help
96 | # etc
97 | ```
98 |
99 | ### Alternative, bootstrap and run manually
100 |
101 |
102 | Steps
103 |
104 |
105 | If for some reason you can't use the `castle` CLI, you can run all of the necessary steps yourself.
106 |
107 | ### 2b. Initialize the internal root CA
108 | ```shell
109 | docker compose -f bootstrap.yml up root-ca -d
110 | export ROOT_CA_CASTLE=$(docker compose -f bootstrap.yml ps -q)
111 | sleep 1
112 | docker cp $ROOT_CA_CASTLE:/home/step/templates volumes/root-ca/
113 | docker cp $ROOT_CA_CASTLE:/home/step/secrets volumes/root-ca/
114 | docker cp $ROOT_CA_CASTLE:/home/step/db volumes/root-ca/
115 | docker cp $ROOT_CA_CASTLE:/home/step/config volumes/root-ca/
116 | docker cp $ROOT_CA_CASTLE:/home/step/certs volumes/root-ca/
117 | docker compose -f bootstrap.yml down
118 | cp volumes/ca.json volumes/root-ca/config/ca.json -f
119 | ```
120 |
121 | And on *nix, set the file permissions so containers can access them:
122 | ```shell
123 | find volumes/root-ca -type d -exec chmod 755 {} +
124 | find volumes/root-ca -type f -exec chmod 644 {} +
125 | ```
126 |
127 | This will configure the internal Smallstep CA, and will generate a number of secrets that you should maintain. If you need to regenerate any of these secrets, you can delete everything in the `./volumes/root-ca/` except the `.gitignore` file.
128 |
129 | ### 3. Prepare your host system
130 |
131 | #### Provide docker compose env vars
132 |
133 | Create a local env file for docker compose. This allows the traefik proxy to read labels on the containers, and route to them accordingly.
134 |
135 | ```shell
136 | DOCKER_PATH=$(sed -e 's|^.*://||' <<< $DOCKER_HOST)
137 | echo "DOCKER_PATH=${DOCKER_PATH}" > .env
138 | ```
139 |
140 | ### 4. Run everything
141 |
142 | #### Using Podman
143 |
144 | This step will build new images that are configured to trust the root certificate authority you just created. Podman can build and run these images just fine, but podman compose doesn't set the right options to build the images. So, to use podman, you should build the images yourself, instead of relying on podman compose to do it. That can be done with the following commands.
145 |
146 | ```shell
147 | podman build . -f proxy.Dockerfile -t localhost/traefik-sandcastle:latest
148 | podman build . -f mastodon.Dockerfile -t localhost/mastodon-sandcastle:latest --target mastodon
149 | podman build . -f mastodon.Dockerfile -t localhost/mastodon-sandcastle:latest --target mastodon-streaming
150 | ```
151 |
152 | #### Compose
153 |
154 | This will re-build the service images with built-in trust for your new internal root CA. This allows all of the services to federate with each other with no additional modifications. The re-build is only necessary once, or whenever a service is updated. You can run only the services you want by specifying their overlay files as extra `-f` args to `docker compose up`
155 | ```shell
156 | # add other *.castle.yml as needed
157 | docker compose -f docker-compose.yml -f mastodon.castle.yml -f sharkey.castle.yml \
158 | up -d
159 | ```
160 |
161 | If you need to rebuild these images because you regenerated the root CA secrets, you can do so by adding the `--build` and `--force-recreate` flags to the compose command.
162 | ```shell
163 | # add other *.castle.yml as needed
164 | docker compose -f docker-compose.yml -f mastodon.castle.yml -f sharkey.castle.yml \
165 | up --build --force-recreate -d
166 | ```
167 |
168 | At this point, you have a functioning sandbox full of fedi services that can all federate with each other. To make this maximally useful to you for local development of your own fedi service, continue on to the following optional steps.
169 |
170 |
171 | ### Add .castle domains to your local hosts file (Optional)
172 | Each of the components provided by this project is configured to serve from it's own .castle domain (ie. mastodon.castle, letterbook.castle, etc). To interact with them from your host (outside of any docker container) you should add these to your system's hosts file.
173 | ```ini
174 | # C:\Windows\System32\drivers\etc\hosts
175 | # OR
176 | # /etc/hosts
177 | 127.0.0.1 proxy.castle
178 | 127.0.0.1 mastodon.castle
179 | 127.0.0.1 letterbook.castle
180 | #etc
181 | ```
182 |
183 | They'll all be available on port `8443`
184 |
185 | ### Add your internal CA as a trusted CA on your host (Optional)
186 |
187 | > [!Warning]
188 | > This is a security risk, if your CA's private key is ever compromised
189 |
190 | This requires having the [`step` cli](https://smallstep.com/docs/step-cli/reference/certificate/) installed on your host machine. After this step, your computer will trust TLS certificates issued by your internal sandcastles CA, just like it was a well known certificate authority like Verisign or Let's Encrypt. In the bootstrapping step, you generated a private key to be used by this CA to sign those TLS certificates. Anyone with access to that key can issue certificates that your computer will trust, even if they're fraudulent. This means another server with access to that key could impersonate other services, like gmail or banks. Keep that key safe.
191 | ```shell
192 | step certificate install --all volumes/root-ca/certs/root_ca.crt
193 | ```
194 |
195 | #### Alternatively
196 | You might not have to configure system-wide trust for the sandcastle internal CA. Many stacks have a way to provide a custom CA bundle that can be used to validate certificates on HTTP requests. For example:
197 |
198 | - Nodejs
199 | `export NODE_EXTRA_CA_CERTS=/path/to/volumes/root-ca/certs/root_ca.crt`
200 |
201 |
202 | ### Revoke the internal CA (Optional)
203 | If you need to revoke trust in the Sandcastles CA, you can use [`step` cli](https://smallstep.com/docs/step-cli/reference/certificate/uninstall/) again.
204 |
205 | ```shell
206 | step certificate uninstall --all volumes/root-ca/certs/root_ca.crt
207 | ```
208 |
209 | # Contributing
210 |
211 | Please contribute! There's so much fedi software out there! If you build or host some sort of fedi server, it would be so helpful for you to share some configurations that make it easy to spin up a test instance of that software in the sandbox. In the absence of a reference implementation or a test suite, this kind of integration sandbox might be our best resource for building new apps and improving cross-app federation support.
212 |
213 | I no longer have ready access to a Windows machine. I'll do what I can to maintain Windows compatibility, but help is appreciated.
214 |
215 | ## Adding New Backends
216 |
217 | There's usually two components to adding a new backend component to the sandcastle environment: a dockerfile, and a docker compose file. They must be named with the same prefix, because the `castle` CLI will naively search for and use them by file name. If a project requires multiple images to send messages to peer services, they will each need a dockerfile, so that they can be configured to trust the internal CA. In that case, you can append a suffix to the names of other necessary dockerfiles, like `my_service.Dockerfile` and `my_service-worker.Dockerfile`.
218 |
219 | The docker compose file must provide additional configuration to the central Traefik proxy, in order to make the server accessible to peer services. This comes in the form of container labels that will configure routing, and a network alias that will configure internal DNS.
220 |
221 | The `castle` CLI includes a `new` command that will scaffold these necessary files. For example:
222 |
223 | ```shell
224 | ./castle new my_serice
225 | ```
226 |
227 | From there, you can customize the generated Dockerfile and compose.yml files. If you think any new components you add would be generally useful to other developers, please consider submitting them in a PR!
228 |
229 | ### Contributing New Components
230 |
231 | One of the goals for this project to have as close to zero required configuration as possible. The intent is to let developers interact with federated peer software. We don't want to force them to become experts in each of those project's operational quirks. So, new backend components should self-initialize as much as possible. If the app requires any migrations, secrets, or configuration, that should be included, with useful defaults. If at all possible, you should also provide a default user and password, so that developers can quickly log in and start generating test federated messages to examine.
232 |
--------------------------------------------------------------------------------
/sharkey.compose.yml~:
--------------------------------------------------------------------------------
1 | # Copyright © 2014-2024 syuilo and contributors
2 | # Configuration adapted from
3 | # https://activitypub.software/TransFem-org/Sharkey/-/blob/7dfe9087b2580e97ea0eb990c94e98624b76c9de/docker-compose_example.yml
4 |
5 | # must be used as an overlay for the main docker-compose file. Ex
6 | # docker-compose -f docker-compose.yml -f sharkey.castle.yml up
7 |
8 | services:
9 | sharkey:
10 | container_name: sharkey.castle
11 | labels:
12 | - traefik.http.routers.sharkey.rule=Host(`sharkey.castle`)
13 | - traefik.http.routers.sharkey.tls=true
14 | - traefik.http.routers.sharkey.tls.certresolver=smallstep
15 | - traefik.http.services.sharkey-sandcastles.loadBalancer.healthCheck.path=/health
16 | - traefik.port=3000
17 | - traefik.enable=true
18 | - traefik.docker.network=fediverse
19 | image: registry.activitypub.software/transfem-org/sharkey:2024.5.1
20 | environment:
21 | - NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/root_ca.crt
22 | restart: always
23 | links:
24 | - sharkey_db
25 | - sharkey_redis
26 | depends_on:
27 | sharkey_db:
28 | condition: service_healthy
29 | sharkey_redis:
30 | condition: service_healthy
31 | ports:
32 | - "3000"
33 | networks:
34 | - shonk
35 | - fediverse
36 | - default
37 | volumes:
38 | - sharkey_data:/sharkey/files
39 | - ./volumes/sharkey/:/sharkey/.config:ro
40 | - ./volumes/root-ca/certs/root_ca.crt:/usr/local/share/ca-certificates/root_ca.crt
41 |
42 | sharkey_redis:
43 | restart: always
44 | image: redis:7-alpine
45 | networks:
46 | - shonk
47 | volumes:
48 | - sharkey_redis:/data
49 | healthcheck:
50 | test: "redis-cli ping"
51 | interval: 5s
52 | retries: 20
53 |
54 | sharkey_db:
55 | restart: always
56 | image: postgres:15-alpine
57 | networks:
58 | - shonk
59 | env_file:
60 | - ./volumes/sharkey/docker.env
61 | volumes:
62 | - sharkey_db:/var/lib/postgresql/data
63 | healthcheck:
64 | test: "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"
65 | interval: 5s
66 | retries: 20
67 |
68 | # mcaptcha:
69 | # restart: always
70 | # image: mcaptcha/mcaptcha:latest
71 | # networks:
72 | # internal_network:
73 | # external_network:
74 | # aliases:
75 | # - localhost
76 | # ports:
77 | # - 7493:7493
78 | # env_file:
79 | # - .config/docker.env
80 | # environment:
81 | # PORT: 7493
82 | # MCAPTCHA_redis_URL: "redis://mcaptcha_redis/"
83 | # depends_on:
84 | # db:
85 | # condition: service_healthy
86 | # mcaptcha_redis:
87 | # condition: service_healthy
88 | #
89 | # mcaptcha_redis:
90 | # image: mcaptcha/cache:latest
91 | # networks:
92 | # - internal_network
93 | # healthcheck:
94 | # test: "redis-cli ping"
95 | # interval: 5s
96 | # retries: 20
97 |
98 | # meilisearch:
99 | # restart: always
100 | # image: getmeili/meilisearch:v1.3.4
101 | # environment:
102 | # - MEILI_NO_ANALYTICS=true
103 | # - MEILI_ENV=production
104 | # - MEILI_MASTER_KEY=ChangeThis
105 | # networks:
106 | # - shonk
107 | # volumes:
108 | # - ./meili_data:/meili_data
109 |
110 | volumes:
111 | sharkey_data:
112 | driver: local
113 | sharkey_db:
114 | driver: local
115 | sharkey_redis:
116 | driver: local
117 |
118 | networks:
119 | shonk:
120 |
--------------------------------------------------------------------------------
/templates/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM docker.io/smallstep/step-cli:0.25.0 AS step-cli
2 |
3 | FROM IMAGE FOR {{app_name}} as {{app_name}}
4 |
5 | USER root
6 |
7 | COPY --from=step-cli /usr/local/bin/step /usr/local/bin/step
8 | COPY volumes/root-ca/certs/root_ca.crt /usr/local/share/ca-certificates/root_ca.crt
9 | RUN /usr/local/bin/step certificate install /usr/local/share/ca-certificates/root_ca.crt
10 |
11 | # perform any other build steps you might need
12 |
--------------------------------------------------------------------------------
/templates/compose.yml:
--------------------------------------------------------------------------------
1 | # must be used as an overlay for the main docker-compose file. Ex
2 | # docker-compose -f docker-compose.yml -f {{app_name}}.castle.yml up
3 |
4 | # defaults
5 | # handle:
6 | # admin:
7 | # password:
8 |
9 | services:
10 | {{app_name}}:
11 | labels:
12 | - traefik.http.routers.{{app_name}}.rule=Host(`{{app_name}}.castle`)
13 | - traefik.http.routers.{{app_name}}.tls=true
14 | - traefik.http.routers.{{app_name}}.tls.certresolver=smallstep
15 | - traefik.http.services.{{app_name}}-sandcastles.loadBalancer.healthCheck.path=
16 | - traefik.port=
17 | - traefik.enable=true
18 | - traefik.docker.network=sandcastles_{{app_name}}
19 | image: localhost/sandcastles/{{app_name}}:latest
20 | build:
21 | dockerfile: ./{{app_name}}.Dockerfile
22 | target: {{app_name}}
23 | tags:
24 | - localhost/sandcastles/{{app_name}}:latest
25 | command:
26 | ports:
27 | - '2999:'
28 |
29 | proxy:
30 | networks:
31 | default:
32 | aliases:
33 | - {{app_name}}.castle
34 | {{app_name}}: {}
35 |
36 | networks:
37 | {{app_name}}:
38 | default:
39 |
--------------------------------------------------------------------------------
/traefik.Dockerfile:
--------------------------------------------------------------------------------
1 | FROM docker.io/traefik:v3.3
2 |
3 | COPY volumes/root-ca/certs/root_ca.crt /usr/local/share/ca-certificates/root_ca.crt
4 |
5 | RUN cat /usr/local/share/ca-certificates/root_ca.crt >> /etc/ssl/certs/ca-certificates.crt
6 |
--------------------------------------------------------------------------------
/units/redir443.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Redirect tcp port 443 to 8443 with redir
3 |
4 | [Service]
5 | ExecStart=/bin/redir -sn -I https 127.0.0.1:443 127.0.0.1:8443
6 |
7 | [Install]
8 | WantedBy=multi-user.target
9 |
--------------------------------------------------------------------------------
/units/redir80.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Redirect tcp port 80 to 8080 with redir
3 |
4 | [Service]
5 | ExecStart=/bin/redir -sn -I http 127.0.0.1:80 127.0.0.1:8080
6 |
7 | [Install]
8 | WantedBy=multi-user.target
9 |
--------------------------------------------------------------------------------
/volumes/ca.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": "/home/step/certs/root_ca.crt",
3 | "federatedRoots": null,
4 | "crt": "/home/step/certs/intermediate_ca.crt",
5 | "key": "/home/step/secrets/intermediate_ca_key",
6 | "address": ":9000",
7 | "insecureAddress": "",
8 | "dnsNames": [
9 | "root-ca.castle",
10 | "root-ca",
11 | "localhost"
12 | ],
13 | "logger": {
14 | "format": "text"
15 | },
16 | "db": {
17 | "type": "badgerv2",
18 | "dataSource": "/home/step/db",
19 | "badgerFileLoadingMode": ""
20 | },
21 | "authority": {
22 | "enableAdmin": true,
23 | "policy": {
24 | "x509": {
25 | "allow": {
26 | "dns": ["*.castle"]
27 | }
28 | }
29 | }
30 | },
31 | "tls": {
32 | "cipherSuites": [
33 | "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
34 | "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
35 | ],
36 | "minVersion": 1.2,
37 | "maxVersion": 1.3,
38 | "renegotiation": false
39 | }
40 | }
--------------------------------------------------------------------------------
/volumes/hosts:
--------------------------------------------------------------------------------
1 | # Default minimal hosts file to be provided to containers
2 | # Podman uses the local machine's hosts file by default, which can cause problems if you have conflicting entries with
3 | # a sandcastle network alias
4 | # Loopback entries; do not change.
5 | # For historical reasons, localhost precedes localhost.localdomain:
6 | 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
7 | ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
8 |
--------------------------------------------------------------------------------
/volumes/mastodon/init.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -eux
4 |
5 | INIT_FILE=/mastodon/public/system/.init_done
6 |
7 | if [ -e $INIT_FILE ] && [ "${FORCE_INIT:-false}" != "true" ]; then
8 | exit 0
9 | fi
10 |
11 | # migrate the db
12 | bundle exec rake db:migrate
13 | sleep 1
14 |
15 | # create the initial user account
16 | tootctl accounts create "${MASTODON_ADMIN_ACCOUNT:=castle_mastodon}" \
17 | --email "${MASTODON_ADMIN_EMAIL:-castle@mastodon.castle}" \
18 | --confirmed \
19 | --role=Owner
20 |
21 | tootctl accounts approve "${MASTODON_ADMIN_ACCOUNT}"
22 |
23 | # set the user password to a known value
24 | echo "user = User.find(1)
25 | user.reset_password!
26 | user.reset_password('${MASTODON_ADMIN_PASSWORD:-password}', '${MASTODON_ADMIN_PASSWORD:-password}')" | rails c
27 |
28 | # mark initialization as complete, so it doesn't have to be repeated on the next startup
29 | touch $INIT_FILE
30 |
--------------------------------------------------------------------------------
/volumes/pasture/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
--------------------------------------------------------------------------------
/volumes/postgresql.conf:
--------------------------------------------------------------------------------
1 | # -----------------------------
2 | # PostgreSQL configuration file
3 | # -----------------------------
4 | #
5 | # This file consists of lines of the form:
6 | #
7 | # name = value
8 | #
9 | # (The "=" is optional.) Whitespace may be used. Comments are introduced with
10 | # "#" anywhere on a line. The complete list of parameter names and allowed
11 | # values can be found in the PostgreSQL documentation.
12 | #
13 | # The commented-out settings shown in this file represent the default values.
14 | # Re-commenting a setting is NOT sufficient to revert it to the default value;
15 | # you need to reload the server.
16 | #
17 | # This file is read on server startup and when the server receives a SIGHUP
18 | # signal. If you edit the file on a running system, you have to SIGHUP the
19 | # server for the changes to take effect, run "pg_ctl reload", or execute
20 | # "SELECT pg_reload_conf()". Some parameters, which are marked below,
21 | # require a server shutdown and restart to take effect.
22 | #
23 | # Any parameter can also be given as a command-line option to the server, e.g.,
24 | # "postgres -c log_connections=on". Some parameters can be changed at run time
25 | # with the "SET" SQL command.
26 | #
27 | # Memory units: B = bytes Time units: us = microseconds
28 | # kB = kilobytes ms = milliseconds
29 | # MB = megabytes s = seconds
30 | # GB = gigabytes min = minutes
31 | # TB = terabytes h = hours
32 | # d = days
33 |
34 |
35 | #------------------------------------------------------------------------------
36 | # FILE LOCATIONS
37 | #------------------------------------------------------------------------------
38 |
39 | # The default values of these variables are driven from the -D command-line
40 | # option or PGDATA environment variable, represented here as ConfigDir.
41 |
42 | #data_directory = 'ConfigDir' # use data in another directory
43 | # (change requires restart)
44 | # hba_file = '/etc/postgresql/pg_hba.conf' # host-based authentication file
45 | # (change requires restart)
46 | #ident_file = 'ConfigDir/pg_ident.conf' # ident configuration file
47 | # (change requires restart)
48 |
49 | # If external_pid_file is not explicitly set, no extra PID file is written.
50 | #external_pid_file = '' # write an extra PID file
51 | # (change requires restart)
52 |
53 |
54 | #------------------------------------------------------------------------------
55 | # CONNECTIONS AND AUTHENTICATION
56 | #------------------------------------------------------------------------------
57 |
58 | # - Connection Settings -
59 |
60 | listen_addresses = '*'
61 | # comma-separated list of addresses;
62 | # defaults to 'localhost'; use '*' for all
63 | # (change requires restart)
64 | #port = 5432 # (change requires restart)
65 | max_connections = 100 # (change requires restart)
66 | #superuser_reserved_connections = 3 # (change requires restart)
67 | #unix_socket_directories = '/var/run/postgresql' # comma-separated list of directories
68 | # (change requires restart)
69 | #unix_socket_group = '' # (change requires restart)
70 | #unix_socket_permissions = 0777 # begin with 0 to use octal notation
71 | # (change requires restart)
72 | #bonjour = off # advertise server via Bonjour
73 | # (change requires restart)
74 | #bonjour_name = '' # defaults to the computer name
75 | # (change requires restart)
76 |
77 | # - TCP settings -
78 | # see "man tcp" for details
79 |
80 | #tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds;
81 | # 0 selects the system default
82 | #tcp_keepalives_interval = 0 # TCP_KEEPINTVL, in seconds;
83 | # 0 selects the system default
84 | #tcp_keepalives_count = 0 # TCP_KEEPCNT;
85 | # 0 selects the system default
86 | #tcp_user_timeout = 0 # TCP_USER_TIMEOUT, in milliseconds;
87 | # 0 selects the system default
88 |
89 | #client_connection_check_interval = 0 # time between checks for client
90 | # disconnection while running queries;
91 | # 0 for never
92 |
93 | # - Authentication -
94 |
95 | #authentication_timeout = 1min # 1s-600s
96 | #password_encryption = scram-sha-256 # scram-sha-256 or md5
97 | #db_user_namespace = off
98 |
99 | # GSSAPI using Kerberos
100 | #krb_server_keyfile = 'FILE:${sysconfdir}/krb5.keytab'
101 | #krb_caseins_users = off
102 |
103 | # - SSL -
104 |
105 | #ssl = off
106 | #ssl_ca_file = ''
107 | #ssl_cert_file = 'server.crt'
108 | #ssl_crl_file = ''
109 | #ssl_crl_dir = ''
110 | #ssl_key_file = 'server.key'
111 | #ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
112 | #ssl_prefer_server_ciphers = on
113 | #ssl_ecdh_curve = 'prime256v1'
114 | #ssl_min_protocol_version = 'TLSv1.2'
115 | #ssl_max_protocol_version = ''
116 | #ssl_dh_params_file = ''
117 | #ssl_passphrase_command = ''
118 | #ssl_passphrase_command_supports_reload = off
119 |
120 |
121 | #------------------------------------------------------------------------------
122 | # RESOURCE USAGE (except WAL)
123 | #------------------------------------------------------------------------------
124 |
125 | # - Memory -
126 |
127 | shared_buffers = 128MB # min 128kB
128 | # (change requires restart)
129 | #huge_pages = try # on, off, or try
130 | # (change requires restart)
131 | #huge_page_size = 0 # zero for system default
132 | # (change requires restart)
133 | #temp_buffers = 8MB # min 800kB
134 | #max_prepared_transactions = 0 # zero disables the feature
135 | # (change requires restart)
136 | # Caution: it is not advisable to set max_prepared_transactions nonzero unless
137 | # you actively intend to use prepared transactions.
138 | #work_mem = 4MB # min 64kB
139 | #hash_mem_multiplier = 2.0 # 1-1000.0 multiplier on hash table work_mem
140 | #maintenance_work_mem = 64MB # min 1MB
141 | #autovacuum_work_mem = -1 # min 1MB, or -1 to use maintenance_work_mem
142 | #logical_decoding_work_mem = 64MB # min 64kB
143 | #max_stack_depth = 2MB # min 100kB
144 | #shared_memory_type = mmap # the default is the first option
145 | # supported by the operating system:
146 | # mmap
147 | # sysv
148 | # windows
149 | # (change requires restart)
150 | dynamic_shared_memory_type = posix # the default is usually the first option
151 | # supported by the operating system:
152 | # posix
153 | # sysv
154 | # windows
155 | # mmap
156 | # (change requires restart)
157 | #min_dynamic_shared_memory = 0MB # (change requires restart)
158 |
159 | # - Disk -
160 |
161 | #temp_file_limit = -1 # limits per-process temp file space
162 | # in kilobytes, or -1 for no limit
163 |
164 | # - Kernel Resources -
165 |
166 | #max_files_per_process = 1000 # min 64
167 | # (change requires restart)
168 |
169 | # - Cost-Based Vacuum Delay -
170 |
171 | #vacuum_cost_delay = 0 # 0-100 milliseconds (0 disables)
172 | #vacuum_cost_page_hit = 1 # 0-10000 credits
173 | #vacuum_cost_page_miss = 2 # 0-10000 credits
174 | #vacuum_cost_page_dirty = 20 # 0-10000 credits
175 | #vacuum_cost_limit = 200 # 1-10000 credits
176 |
177 | # - Background Writer -
178 |
179 | #bgwriter_delay = 200ms # 10-10000ms between rounds
180 | #bgwriter_lru_maxpages = 100 # max buffers written/round, 0 disables
181 | #bgwriter_lru_multiplier = 2.0 # 0-10.0 multiplier on buffers scanned/round
182 | #bgwriter_flush_after = 512kB # measured in pages, 0 disables
183 |
184 | # - Asynchronous Behavior -
185 |
186 | #backend_flush_after = 0 # measured in pages, 0 disables
187 | #effective_io_concurrency = 1 # 1-1000; 0 disables prefetching
188 | #maintenance_io_concurrency = 10 # 1-1000; 0 disables prefetching
189 | #max_worker_processes = 8 # (change requires restart)
190 | #max_parallel_workers_per_gather = 2 # taken from max_parallel_workers
191 | #max_parallel_maintenance_workers = 2 # taken from max_parallel_workers
192 | #max_parallel_workers = 8 # maximum number of max_worker_processes that
193 | # can be used in parallel operations
194 | #parallel_leader_participation = on
195 | #old_snapshot_threshold = -1 # 1min-60d; -1 disables; 0 is immediate
196 | # (change requires restart)
197 |
198 |
199 | #------------------------------------------------------------------------------
200 | # WRITE-AHEAD LOG
201 | #------------------------------------------------------------------------------
202 |
203 | # - Settings -
204 |
205 | #wal_level = replica # minimal, replica, or logical
206 | # (change requires restart)
207 | #fsync = on # flush data to disk for crash safety
208 | # (turning this off can cause
209 | # unrecoverable data corruption)
210 | #synchronous_commit = on # synchronization level;
211 | # off, local, remote_write, remote_apply, or on
212 | #wal_sync_method = fsync # the default is the first option
213 | # supported by the operating system:
214 | # open_datasync
215 | # fdatasync (default on Linux and FreeBSD)
216 | # fsync
217 | # fsync_writethrough
218 | # open_sync
219 | #full_page_writes = on # recover from partial page writes
220 | #wal_log_hints = off # also do full page writes of non-critical updates
221 | # (change requires restart)
222 | #wal_compression = off # enables compression of full-page writes;
223 | # off, pglz, lz4, zstd, or on
224 | #wal_init_zero = on # zero-fill new WAL files
225 | #wal_recycle = on # recycle WAL files
226 | #wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers
227 | # (change requires restart)
228 | #wal_writer_delay = 200ms # 1-10000 milliseconds
229 | #wal_writer_flush_after = 1MB # measured in pages, 0 disables
230 | #wal_skip_threshold = 2MB
231 |
232 | #commit_delay = 0 # range 0-100000, in microseconds
233 | #commit_siblings = 5 # range 1-1000
234 |
235 | # - Checkpoints -
236 |
237 | #checkpoint_timeout = 5min # range 30s-1d
238 | #checkpoint_completion_target = 0.9 # checkpoint target duration, 0.0 - 1.0
239 | #checkpoint_flush_after = 256kB # measured in pages, 0 disables
240 | #checkpoint_warning = 30s # 0 disables
241 | max_wal_size = 1GB
242 | min_wal_size = 80MB
243 |
244 | # - Prefetching during recovery -
245 |
246 | #recovery_prefetch = try # prefetch pages referenced in the WAL?
247 | #wal_decode_buffer_size = 512kB # lookahead window used for prefetching
248 | # (change requires restart)
249 |
250 | # - Archiving -
251 |
252 | #archive_mode = off # enables archiving; off, on, or always
253 | # (change requires restart)
254 | #archive_library = '' # library to use to archive a logfile segment
255 | # (empty string indicates archive_command should
256 | # be used)
257 | #archive_command = '' # command to use to archive a logfile segment
258 | # placeholders: %p = path of file to archive
259 | # %f = file name only
260 | # e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f'
261 | #archive_timeout = 0 # force a logfile segment switch after this
262 | # number of seconds; 0 disables
263 |
264 | # - Archive Recovery -
265 |
266 | # These are only used in recovery mode.
267 |
268 | #restore_command = '' # command to use to restore an archived logfile segment
269 | # placeholders: %p = path of file to restore
270 | # %f = file name only
271 | # e.g. 'cp /mnt/server/archivedir/%f %p'
272 | #archive_cleanup_command = '' # command to execute at every restartpoint
273 | #recovery_end_command = '' # command to execute at completion of recovery
274 |
275 | # - Recovery Target -
276 |
277 | # Set these only when performing a targeted recovery.
278 |
279 | #recovery_target = '' # 'immediate' to end recovery as soon as a
280 | # consistent state is reached
281 | # (change requires restart)
282 | #recovery_target_name = '' # the named restore point to which recovery will proceed
283 | # (change requires restart)
284 | #recovery_target_time = '' # the time stamp up to which recovery will proceed
285 | # (change requires restart)
286 | #recovery_target_xid = '' # the transaction ID up to which recovery will proceed
287 | # (change requires restart)
288 | #recovery_target_lsn = '' # the WAL LSN up to which recovery will proceed
289 | # (change requires restart)
290 | #recovery_target_inclusive = on # Specifies whether to stop:
291 | # just after the specified recovery target (on)
292 | # just before the recovery target (off)
293 | # (change requires restart)
294 | #recovery_target_timeline = 'latest' # 'current', 'latest', or timeline ID
295 | # (change requires restart)
296 | #recovery_target_action = 'pause' # 'pause', 'promote', 'shutdown'
297 | # (change requires restart)
298 |
299 |
300 | #------------------------------------------------------------------------------
301 | # REPLICATION
302 | #------------------------------------------------------------------------------
303 |
304 | # - Sending Servers -
305 |
306 | # Set these on the primary and on any standby that will send replication data.
307 |
308 | #max_wal_senders = 10 # max number of walsender processes
309 | # (change requires restart)
310 | #max_replication_slots = 10 # max number of replication slots
311 | # (change requires restart)
312 | #wal_keep_size = 0 # in megabytes; 0 disables
313 | #max_slot_wal_keep_size = -1 # in megabytes; -1 disables
314 | #wal_sender_timeout = 60s # in milliseconds; 0 disables
315 | #track_commit_timestamp = off # collect timestamp of transaction commit
316 | # (change requires restart)
317 |
318 | # - Primary Server -
319 |
320 | # These settings are ignored on a standby server.
321 |
322 | #synchronous_standby_names = '' # standby servers that provide sync rep
323 | # method to choose sync standbys, number of sync standbys,
324 | # and comma-separated list of application_name
325 | # from standby(s); '*' = all
326 | #vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed
327 |
328 | # - Standby Servers -
329 |
330 | # These settings are ignored on a primary server.
331 |
332 | #primary_conninfo = '' # connection string to sending server
333 | #primary_slot_name = '' # replication slot on sending server
334 | #promote_trigger_file = '' # file name whose presence ends recovery
335 | #hot_standby = on # "off" disallows queries during recovery
336 | # (change requires restart)
337 | #max_standby_archive_delay = 30s # max delay before canceling queries
338 | # when reading WAL from archive;
339 | # -1 allows indefinite delay
340 | #max_standby_streaming_delay = 30s # max delay before canceling queries
341 | # when reading streaming WAL;
342 | # -1 allows indefinite delay
343 | #wal_receiver_create_temp_slot = off # create temp slot if primary_slot_name
344 | # is not set
345 | #wal_receiver_status_interval = 10s # send replies at least this often
346 | # 0 disables
347 | #hot_standby_feedback = off # send info from standby to prevent
348 | # query conflicts
349 | #wal_receiver_timeout = 60s # time that receiver waits for
350 | # communication from primary
351 | # in milliseconds; 0 disables
352 | #wal_retrieve_retry_interval = 5s # time to wait before retrying to
353 | # retrieve WAL after a failed attempt
354 | #recovery_min_apply_delay = 0 # minimum delay for applying changes during recovery
355 |
356 | # - Subscribers -
357 |
358 | # These settings are ignored on a publisher.
359 |
360 | #max_logical_replication_workers = 4 # taken from max_worker_processes
361 | # (change requires restart)
362 | #max_sync_workers_per_subscription = 2 # taken from max_logical_replication_workers
363 |
364 |
365 | #------------------------------------------------------------------------------
366 | # QUERY TUNING
367 | #------------------------------------------------------------------------------
368 |
369 | # - Planner Method Configuration -
370 |
371 | #enable_async_append = on
372 | #enable_bitmapscan = on
373 | #enable_gathermerge = on
374 | #enable_hashagg = on
375 | #enable_hashjoin = on
376 | #enable_incremental_sort = on
377 | #enable_indexscan = on
378 | #enable_indexonlyscan = on
379 | #enable_material = on
380 | #enable_memoize = on
381 | #enable_mergejoin = on
382 | #enable_nestloop = on
383 | #enable_parallel_append = on
384 | #enable_parallel_hash = on
385 | #enable_partition_pruning = on
386 | #enable_partitionwise_join = off
387 | #enable_partitionwise_aggregate = off
388 | #enable_seqscan = on
389 | #enable_sort = on
390 | #enable_tidscan = on
391 |
392 | # - Planner Cost Constants -
393 |
394 | #seq_page_cost = 1.0 # measured on an arbitrary scale
395 | #random_page_cost = 4.0 # same scale as above
396 | #cpu_tuple_cost = 0.01 # same scale as above
397 | #cpu_index_tuple_cost = 0.005 # same scale as above
398 | #cpu_operator_cost = 0.0025 # same scale as above
399 | #parallel_setup_cost = 1000.0 # same scale as above
400 | #parallel_tuple_cost = 0.1 # same scale as above
401 | #min_parallel_table_scan_size = 8MB
402 | #min_parallel_index_scan_size = 512kB
403 | #effective_cache_size = 4GB
404 |
405 | #jit_above_cost = 100000 # perform JIT compilation if available
406 | # and query more expensive than this;
407 | # -1 disables
408 | #jit_inline_above_cost = 500000 # inline small functions if query is
409 | # more expensive than this; -1 disables
410 | #jit_optimize_above_cost = 500000 # use expensive JIT optimizations if
411 | # query is more expensive than this;
412 | # -1 disables
413 |
414 | # - Genetic Query Optimizer -
415 |
416 | #geqo = on
417 | #geqo_threshold = 12
418 | #geqo_effort = 5 # range 1-10
419 | #geqo_pool_size = 0 # selects default based on effort
420 | #geqo_generations = 0 # selects default based on effort
421 | #geqo_selection_bias = 2.0 # range 1.5-2.0
422 | #geqo_seed = 0.0 # range 0.0-1.0
423 |
424 | # - Other Planner Options -
425 |
426 | #default_statistics_target = 100 # range 1-10000
427 | #constraint_exclusion = partition # on, off, or partition
428 | #cursor_tuple_fraction = 0.1 # range 0.0-1.0
429 | #from_collapse_limit = 8
430 | #jit = on # allow JIT compilation
431 | #join_collapse_limit = 8 # 1 disables collapsing of explicit
432 | # JOIN clauses
433 | #plan_cache_mode = auto # auto, force_generic_plan or
434 | # force_custom_plan
435 | #recursive_worktable_factor = 10.0 # range 0.001-1000000
436 |
437 |
438 | #------------------------------------------------------------------------------
439 | # REPORTING AND LOGGING
440 | #------------------------------------------------------------------------------
441 |
442 | # - Where to Log -
443 |
444 | #log_destination = 'stderr' # Valid values are combinations of
445 | # stderr, csvlog, jsonlog, syslog, and
446 | # eventlog, depending on platform.
447 | # csvlog and jsonlog require
448 | # logging_collector to be on.
449 |
450 | # This is used when logging to stderr:
451 | #logging_collector = off # Enable capturing of stderr, jsonlog,
452 | # and csvlog into log files. Required
453 | # to be on for csvlogs and jsonlogs.
454 | # (change requires restart)
455 |
456 | # These are only used if logging_collector is on:
457 | #log_directory = 'log' # directory where log files are written,
458 | # can be absolute or relative to PGDATA
459 | #log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # log file name pattern,
460 | # can include strftime() escapes
461 | #log_file_mode = 0600 # creation mode for log files,
462 | # begin with 0 to use octal notation
463 | #log_rotation_age = 1d # Automatic rotation of logfiles will
464 | # happen after that time. 0 disables.
465 | #log_rotation_size = 10MB # Automatic rotation of logfiles will
466 | # happen after that much log output.
467 | # 0 disables.
468 | #log_truncate_on_rotation = off # If on, an existing log file with the
469 | # same name as the new log file will be
470 | # truncated rather than appended to.
471 | # But such truncation only occurs on
472 | # time-driven rotation, not on restarts
473 | # or size-driven rotation. Default is
474 | # off, meaning append to existing files
475 | # in all cases.
476 |
477 | # These are relevant when logging to syslog:
478 | #syslog_facility = 'LOCAL0'
479 | #syslog_ident = 'postgres'
480 | #syslog_sequence_numbers = on
481 | #syslog_split_messages = on
482 |
483 | # This is only relevant when logging to eventlog (Windows):
484 | # (change requires restart)
485 | #event_source = 'PostgreSQL'
486 |
487 | # - When to Log -
488 |
489 | log_min_messages = warning # values in order of decreasing detail:
490 | # debug5
491 | # debug4
492 | # debug3
493 | # debug2
494 | # debug1
495 | # info
496 | # notice
497 | # warning
498 | # error
499 | # log
500 | # fatal
501 | # panic
502 |
503 | log_min_error_statement = warning # values in order of decreasing detail:
504 | # debug5
505 | # debug4
506 | # debug3
507 | # debug2
508 | # debug1
509 | # info
510 | # notice
511 | # warning
512 | # error
513 | # log
514 | # fatal
515 | # panic (effectively off)
516 |
517 | #log_min_duration_statement = -1 # -1 is disabled, 0 logs all statements
518 | # and their durations, > 0 logs only
519 | # statements running at least this number
520 | # of milliseconds
521 |
522 | #log_min_duration_sample = -1 # -1 is disabled, 0 logs a sample of statements
523 | # and their durations, > 0 logs only a sample of
524 | # statements running at least this number
525 | # of milliseconds;
526 | # sample fraction is determined by log_statement_sample_rate
527 |
528 | #log_statement_sample_rate = 1.0 # fraction of logged statements exceeding
529 | # log_min_duration_sample to be logged;
530 | # 1.0 logs all such statements, 0.0 never logs
531 |
532 |
533 | #log_transaction_sample_rate = 0.0 # fraction of transactions whose statements
534 | # are logged regardless of their duration; 1.0 logs all
535 | # statements from all transactions, 0.0 never logs
536 |
537 | #log_startup_progress_interval = 10s # Time between progress updates for
538 | # long-running startup operations.
539 | # 0 disables the feature, > 0 indicates
540 | # the interval in milliseconds.
541 |
542 | # - What to Log -
543 |
544 | #debug_print_parse = off
545 | #debug_print_rewritten = off
546 | #debug_print_plan = off
547 | #debug_pretty_print = on
548 | #log_autovacuum_min_duration = 10min # log autovacuum activity;
549 | # -1 disables, 0 logs all actions and
550 | # their durations, > 0 logs only
551 | # actions running at least this number
552 | # of milliseconds.
553 | #log_checkpoints = on
554 | log_connections = on
555 | log_disconnections = on
556 | #log_duration = off
557 | #log_error_verbosity = verbose # terse, default, or verbose messages
558 | #log_hostname = off
559 | #log_line_prefix = '%m [%p] ' # special values:
560 | # %a = application name
561 | # %u = user name
562 | # %d = database name
563 | # %r = remote host and port
564 | # %h = remote host
565 | # %b = backend type
566 | # %p = process ID
567 | # %P = process ID of parallel group leader
568 | # %t = timestamp without milliseconds
569 | # %m = timestamp with milliseconds
570 | # %n = timestamp with milliseconds (as a Unix epoch)
571 | # %Q = query ID (0 if none or not computed)
572 | # %i = command tag
573 | # %e = SQL state
574 | # %c = session ID
575 | # %l = session line number
576 | # %s = session start timestamp
577 | # %v = virtual transaction ID
578 | # %x = transaction ID (0 if none)
579 | # %q = stop here in non-session
580 | # processes
581 | # %% = '%'
582 | # e.g. '<%u%%%d> '
583 | #log_lock_waits = off # log lock waits >= deadlock_timeout
584 | #log_recovery_conflict_waits = off # log standby recovery conflict waits
585 | # >= deadlock_timeout
586 | #log_parameter_max_length = -1 # when logging statements, limit logged
587 | # bind-parameter values to N bytes;
588 | # -1 means print in full, 0 disables
589 | #log_parameter_max_length_on_error = 0 # when logging an error, limit logged
590 | # bind-parameter values to N bytes;
591 | # -1 means print in full, 0 disables
592 | log_statement = 'ddl' # none, ddl, mod, all
593 | #log_replication_commands = off
594 | #log_temp_files = -1 # log temporary files equal or larger
595 | # than the specified size in kilobytes;
596 | # -1 disables, 0 logs all temp files
597 | log_timezone = 'UTC'
598 |
599 |
600 | #------------------------------------------------------------------------------
601 | # PROCESS TITLE
602 | #------------------------------------------------------------------------------
603 |
604 | #cluster_name = '' # added to process titles if nonempty
605 | # (change requires restart)
606 | #update_process_title = on
607 |
608 |
609 | #------------------------------------------------------------------------------
610 | # STATISTICS
611 | #------------------------------------------------------------------------------
612 |
613 | # - Cumulative Query and Index Statistics -
614 |
615 | #track_activities = on
616 | #track_activity_query_size = 1024 # (change requires restart)
617 | #track_counts = on
618 | #track_io_timing = off
619 | #track_wal_io_timing = off
620 | #track_functions = none # none, pl, all
621 | #stats_fetch_consistency = cache
622 |
623 |
624 | # - Monitoring -
625 |
626 | #compute_query_id = auto
627 | #log_statement_stats = off
628 | #log_parser_stats = off
629 | #log_planner_stats = off
630 | #log_executor_stats = off
631 |
632 |
633 | #------------------------------------------------------------------------------
634 | # AUTOVACUUM
635 | #------------------------------------------------------------------------------
636 |
637 | #autovacuum = on # Enable autovacuum subprocess? 'on'
638 | # requires track_counts to also be on.
639 | #autovacuum_max_workers = 3 # max number of autovacuum subprocesses
640 | # (change requires restart)
641 | #autovacuum_naptime = 1min # time between autovacuum runs
642 | #autovacuum_vacuum_threshold = 50 # min number of row updates before
643 | # vacuum
644 | #autovacuum_vacuum_insert_threshold = 1000 # min number of row inserts
645 | # before vacuum; -1 disables insert
646 | # vacuums
647 | #autovacuum_analyze_threshold = 50 # min number of row updates before
648 | # analyze
649 | #autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum
650 | #autovacuum_vacuum_insert_scale_factor = 0.2 # fraction of inserts over table
651 | # size before insert vacuum
652 | #autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze
653 | #autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum
654 | # (change requires restart)
655 | #autovacuum_multixact_freeze_max_age = 400000000 # maximum multixact age
656 | # before forced vacuum
657 | # (change requires restart)
658 | #autovacuum_vacuum_cost_delay = 2ms # default vacuum cost delay for
659 | # autovacuum, in milliseconds;
660 | # -1 means use vacuum_cost_delay
661 | #autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for
662 | # autovacuum, -1 means use
663 | # vacuum_cost_limit
664 |
665 |
666 | #------------------------------------------------------------------------------
667 | # CLIENT CONNECTION DEFAULTS
668 | #------------------------------------------------------------------------------
669 |
670 | # - Statement Behavior -
671 |
672 | #client_min_messages = notice # values in order of decreasing detail:
673 | # debug5
674 | # debug4
675 | # debug3
676 | # debug2
677 | # debug1
678 | # log
679 | # notice
680 | # warning
681 | # error
682 | #search_path = '"$user", public' # schema names
683 | #row_security = on
684 | #default_table_access_method = 'heap'
685 | #default_tablespace = '' # a tablespace name, '' uses the default
686 | #default_toast_compression = 'pglz' # 'pglz' or 'lz4'
687 | #temp_tablespaces = '' # a list of tablespace names, '' uses
688 | # only default tablespace
689 | #check_function_bodies = on
690 | #default_transaction_isolation = 'read committed'
691 | #default_transaction_read_only = off
692 | #default_transaction_deferrable = off
693 | #session_replication_role = 'origin'
694 | #statement_timeout = 0 # in milliseconds, 0 is disabled
695 | #lock_timeout = 0 # in milliseconds, 0 is disabled
696 | #idle_in_transaction_session_timeout = 0 # in milliseconds, 0 is disabled
697 | #idle_session_timeout = 0 # in milliseconds, 0 is disabled
698 | #vacuum_freeze_table_age = 150000000
699 | #vacuum_freeze_min_age = 50000000
700 | #vacuum_failsafe_age = 1600000000
701 | #vacuum_multixact_freeze_table_age = 150000000
702 | #vacuum_multixact_freeze_min_age = 5000000
703 | #vacuum_multixact_failsafe_age = 1600000000
704 | #bytea_output = 'hex' # hex, escape
705 | #xmlbinary = 'base64'
706 | #xmloption = 'content'
707 | #gin_pending_list_limit = 4MB
708 |
709 | # - Locale and Formatting -
710 |
711 | datestyle = 'iso, mdy'
712 | #intervalstyle = 'postgres'
713 | timezone = 'UTC'
714 | #timezone_abbreviations = 'Default' # Select the set of available time zone
715 | # abbreviations. Currently, there are
716 | # Default
717 | # Australia (historical usage)
718 | # India
719 | # You can create your own file in
720 | # share/timezonesets/.
721 | #extra_float_digits = 1 # min -15, max 3; any value >0 actually
722 | # selects precise output mode
723 | #client_encoding = sql_ascii # actually, defaults to database
724 | # encoding
725 |
726 | # These settings are initialized by initdb, but they can be changed.
727 | lc_messages = 'en_US.utf8' # locale for system error message
728 | # strings
729 | lc_monetary = 'en_US.utf8' # locale for monetary formatting
730 | lc_numeric = 'en_US.utf8' # locale for number formatting
731 | lc_time = 'en_US.utf8' # locale for time formatting
732 |
733 | # default configuration for text search
734 | default_text_search_config = 'pg_catalog.english'
735 |
736 | # - Shared Library Preloading -
737 |
738 | #local_preload_libraries = ''
739 | #session_preload_libraries = ''
740 | #shared_preload_libraries = '' # (change requires restart)
741 | #jit_provider = 'llvmjit' # JIT library to use
742 |
743 | # - Other Defaults -
744 |
745 | #dynamic_library_path = '$libdir'
746 | #gin_fuzzy_search_limit = 0
747 |
748 |
749 | #------------------------------------------------------------------------------
750 | # LOCK MANAGEMENT
751 | #------------------------------------------------------------------------------
752 |
753 | #deadlock_timeout = 1s
754 | #max_locks_per_transaction = 64 # min 10
755 | # (change requires restart)
756 | #max_pred_locks_per_transaction = 64 # min 10
757 | # (change requires restart)
758 | #max_pred_locks_per_relation = -2 # negative values mean
759 | # (max_pred_locks_per_transaction
760 | # / -max_pred_locks_per_relation) - 1
761 | #max_pred_locks_per_page = 2 # min 0
762 |
763 |
764 | #------------------------------------------------------------------------------
765 | # VERSION AND PLATFORM COMPATIBILITY
766 | #------------------------------------------------------------------------------
767 |
768 | # - Previous PostgreSQL Versions -
769 |
770 | #array_nulls = on
771 | #backslash_quote = safe_encoding # on, off, or safe_encoding
772 | #escape_string_warning = on
773 | #lo_compat_privileges = off
774 | #quote_all_identifiers = off
775 | #standard_conforming_strings = on
776 | #synchronize_seqscans = on
777 |
778 | # - Other Platforms and Clients -
779 |
780 | #transform_null_equals = off
781 |
782 |
783 | #------------------------------------------------------------------------------
784 | # ERROR HANDLING
785 | #------------------------------------------------------------------------------
786 |
787 | #exit_on_error = off # terminate session on any error?
788 | #restart_after_crash = on # reinitialize after backend crash?
789 | #data_sync_retry = off # retry or panic on failure to fsync
790 | # data?
791 | # (change requires restart)
792 | #recovery_init_sync_method = fsync # fsync, syncfs (Linux 5.8+)
793 |
794 |
795 | #--------------------------------------------------------------NewFile1.txt----------------
796 | # CONFIG FILE INCLUDES
797 | #------------------------------------------------------------------------------
798 |
799 | # These options allow settings to be loaded from files other than the
800 | # default postgresql.conf. Note that these are directives, not variable
801 | # assignments, so they can usefully be given more than once.
802 |
803 | #include_dir = '...' # include files ending in '.conf' from
804 | # a directory, e.g., 'conf.d'
805 | #include_if_exists = '...' # include file only if it exists
806 | #include = '...' # include file
807 |
808 |
809 | #------------------------------------------------------------------------------
810 | # CUSTOMIZED OPTIONS
811 | #------------------------------------------------------------------------------
812 |
813 | # Add settings for extensions here
814 |
--------------------------------------------------------------------------------
/volumes/proxy/acme.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Letterbook/Sandcastles/a1157c28a9e9c6483353f389dc16cbba3f81cec2/volumes/proxy/acme.json
--------------------------------------------------------------------------------
/volumes/proxy/traefik.yml:
--------------------------------------------------------------------------------
1 | entryPoints:
2 | web:
3 | address: ':80'
4 | http:
5 | redirections:
6 | entryPoint:
7 | to: websecure
8 | scheme: https
9 | websecure:
10 | address: ':443'
11 | hostwebsecure:
12 | address: 'localhost:8443'
13 |
14 | log:
15 | level: INFO
16 |
17 | accessLog: {}
18 |
19 | api:
20 | dashboard: true
21 | debug: true
22 | insecure: true
23 |
24 | providers:
25 | docker:
26 | watch: true
27 | network: sandcastles_default
28 | endpoint: unix:///var/run/docker.sock
29 | exposedByDefault: false
30 | file:
31 | filename: '/etc/traefik/traefik_dynamic.yml'
32 |
33 | certificatesResolvers:
34 | smallstep:
35 | acme:
36 | caServer: 'https://root-ca.castle:9000/acme/acme/directory'
37 | storage: 'acme.json'
38 | httpChallenge:
39 | entryPoint: web
--------------------------------------------------------------------------------
/volumes/proxy/traefik_dynamic.yml:
--------------------------------------------------------------------------------
1 | http:
2 | middlewares:
3 | simpleAuth:
4 | basicAuth:
5 | users:
6 | # username: sandcastles
7 | # password: admin
8 | - "sandcastles:$apr1$Xe1bQOFU$OQ.6qf4QCcRk5E8mQ.yt4."
9 | routers:
10 | api:
11 | rule: "Host(`proxy.castle`) && PathPrefix(`/dashboard`)"
12 | entrypoints:
13 | - websecure
14 | middlewares:
15 | - simpleAuth
16 | service: 'api@internal'
17 | tls:
18 | certResolver: 'smallstep'
19 | dashboard:
20 | rule: "Host(`localhost`) && PathPrefix(`/dashboard`)"
21 | entrypoints:
22 | - websecure
23 | - web
24 | - hostwebsecure
25 | middlewares:
26 | - simpleAuth
27 | service: 'api@internal'
--------------------------------------------------------------------------------
/volumes/root-ca/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore everything in this dir, except this file
2 | # This dir will contain secrets that you will need to trust on your
3 | *
4 | !.gitignore
5 | !readme.md
--------------------------------------------------------------------------------
/volumes/root-ca/readme.md:
--------------------------------------------------------------------------------
1 | # Warning
2 |
3 | > [!WARNING]
4 | > After setup, this directory will contain secrets that should never be shared with anyone. Doing so could put your computer at risk.
5 |
6 | In order to permit most fedi software to talk to each other with minimal modifications, they need to do it over HTTPS. Which means they all need to have SSL certificates, which all need to be issued by a certificate authority, which they need to trust. That likely includes Letterbook, or any other fediverse application that you're developing on your host machine. Which means you need to trust it as well.
7 |
8 | When you use this project, you're going to create a root certificate authority, so that you can issue those certificates. Most of that happens automatically, and you don't need to be *too* concerned with how it works. But, in order to make full use of it, you will probably have to add that CA as a trusted root certificate authority on your own machine.
9 |
10 | After you've set up the project, this directory will contain the private signing key for that CA, along with a variety of other secrets and configuration data. **Never reveal this data to anyone!** If the signing key of the local root CA you're about to create is ever compromised, it could be used to perform invisible man-in-the-middle attacks against any computer that trusts it. Which likely includes the computer you're using to read this right now.
--------------------------------------------------------------------------------
/volumes/sharkey/default.yml:
--------------------------------------------------------------------------------
1 | #━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
2 | # Misskey configuration
3 | #━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
4 |
5 | # ┌──────────────────────────────┐
6 | #───┘ a boring but important thing └────────────────────────────
7 |
8 | #
9 | # First of all, let me tell you a story that may possibly be
10 | # boring to you and possibly important to you.
11 | #
12 | # Misskey is licensed under the AGPLv3 license. This license is
13 | # known to be often misunderstood. Please read the following
14 | # instructions carefully and select the appropriate option so
15 | # that you do not negligently cause a license violation.
16 | #
17 |
18 | # --------
19 | # Option 1: If you host Misskey AS-IS (without any changes to
20 | # the source code. forks are not included).
21 | #
22 | # Step 1: Congratulations! You don't need to do anything.
23 |
24 | # --------
25 | # Option 2: If you have made changes to the source code (forks
26 | # are included) and publish a Git repository of source
27 | # code. There should be no access restrictions on
28 | # this repository. Strictly speaking, it doesn't have
29 | # to be a Git repository, but you'll probably use Git!
30 | #
31 | # Step 1: Build and run the Misskey server first.
32 | # Step 2: Open in
33 | # your browser with the administrator account.
34 | # Step 3: Enter the URL of your Git repository in the
35 | # "Repository URL" field.
36 |
37 | # --------
38 | # Option 3: If neither of the above applies to you.
39 | # (In this case, the source code should be published
40 | # on the Misskey interface. IT IS NOT ENOUGH TO
41 | # DISCLOSE THE SOURCE CODE WEHN A USER REQUESTS IT BY
42 | # E-MAIL OR OTHER MEANS. If you are not satisfied
43 | # with this, it is recommended that you read the
44 | # license again carefully. Anyway, enabling this
45 | # option will automatically generate and publish a
46 | # tarball at build time, protecting you from
47 | # inadvertent license violations. (There is no legal
48 | # guarantee, of course.) The tarball will generated
49 | # from the root directory of your codebase. So it is
50 | # also recommended to check directory
51 | # once after building and before activating the server
52 | # to avoid ACCIDENTAL LEAKING OF SENSITIVE INFORMATION.
53 | # To prevent certain files from being included in the
54 | # tarball, add a glob pattern after line 15 in
55 | # . DO NOT FORGET TO BUILD AFTER
56 | # ENABLING THIS OPTION!)
57 | #
58 | # Step 1: Uncomment the following line.
59 | #
60 | # publishTarballInsteadOfProvideRepositoryUrl: true
61 |
62 | # ┌─────┐
63 | #───┘ URL └─────────────────────────────────────────────────────
64 |
65 | # Final accessible URL seen by a user.
66 | url: https://sharkey.castle/
67 |
68 | # ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
69 | # URL SETTINGS AFTER THAT!
70 |
71 | # ┌───────────────────────┐
72 | #───┘ Port and TLS settings └───────────────────────────────────
73 |
74 | #
75 | # Misskey requires a reverse proxy to support HTTPS connections.
76 | #
77 | # +----- https://example.tld/ ------------+
78 | # +------+ |+-------------+ +----------------+|
79 | # | User | ---> || Proxy (443) | ---> | Misskey (3000) ||
80 | # +------+ |+-------------+ +----------------+|
81 | # +---------------------------------------+
82 | #
83 | # You need to set up a reverse proxy. (e.g. nginx)
84 | # An encrypted connection with HTTPS is highly recommended
85 | # because tokens may be transferred in GET requests.
86 |
87 | # The port that your Misskey server should listen on.
88 | port: 3000
89 |
90 | # ┌──────────────────────────┐
91 | #───┘ PostgreSQL configuration └────────────────────────────────
92 |
93 | db:
94 | host: sharkey_db
95 | port: 5432
96 |
97 | # Database name
98 | db: misskey
99 |
100 | # Auth
101 | user: example-misskey-user
102 | pass: example-misskey-pass
103 |
104 | # Whether disable Caching queries
105 | #disableCache: true
106 |
107 | # Extra Connection options
108 | #extra:
109 | # ssl: true
110 |
111 | dbReplications: false
112 |
113 | # You can configure any number of replicas here
114 | #dbSlaves:
115 | # -
116 | # host:
117 | # port:
118 | # db:
119 | # user:
120 | # pass:
121 | # -
122 | # host:
123 | # port:
124 | # db:
125 | # user:
126 | # pass:
127 |
128 | # ┌─────────────────────┐
129 | #───┘ Redis configuration └─────────────────────────────────────
130 |
131 | redis:
132 | host: sharkey_redis
133 | port: 6379
134 | #family: 0 # 0=Both, 4=IPv4, 6=IPv6
135 | #pass: example-pass
136 | #prefix: example-prefix
137 | #db: 1
138 |
139 | #redisForPubsub:
140 | # host: redis
141 | # port: 6379
142 | # #family: 0 # 0=Both, 4=IPv4, 6=IPv6
143 | # #pass: example-pass
144 | # #prefix: example-prefix
145 | # #db: 1
146 |
147 | #redisForJobQueue:
148 | # host: redis
149 | # port: 6379
150 | # #family: 0 # 0=Both, 4=IPv4, 6=IPv6
151 | # #pass: example-pass
152 | # #prefix: example-prefix
153 | # #db: 1
154 |
155 | #redisForTimelines:
156 | # host: redis
157 | # port: 6379
158 | # #family: 0 # 0=Both, 4=IPv4, 6=IPv6
159 | # #pass: example-pass
160 | # #prefix: example-prefix
161 | # #db: 1
162 |
163 | # ┌───────────────────────────┐
164 | #───┘ MeiliSearch configuration └─────────────────────────────
165 |
166 | # You can set scope to local (default value) or global
167 | # (include notes from remote).
168 |
169 | #meilisearch:
170 | # host: meilisearch
171 | # port: 7700
172 | # apiKey: ''
173 | # ssl: true
174 | # index: ''
175 | # scope: global
176 |
177 | # ┌───────────────┐
178 | #───┘ ID generation └───────────────────────────────────────────
179 |
180 | # You can select the ID generation method.
181 | # You don't usually need to change this setting, but you can
182 | # change it according to your preferences.
183 |
184 | # Available methods:
185 | # aid ... Short, Millisecond accuracy
186 | # aidx ... Millisecond accuracy
187 | # meid ... Similar to ObjectID, Millisecond accuracy
188 | # ulid ... Millisecond accuracy
189 | # objectid ... This is left for backward compatibility
190 |
191 | # ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
192 | # ID SETTINGS AFTER THAT!
193 |
194 | id: 'aidx'
195 |
196 | # ┌────────────────┐
197 | #───┘ Error tracking └──────────────────────────────────────────
198 |
199 | # Sentry is available for error tracking.
200 | # See the Sentry documentation for more details on options.
201 |
202 | #sentryForBackend:
203 | # enableNodeProfiling: true
204 | # options:
205 | # dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
206 |
207 | #sentryForFrontend:
208 | # options:
209 | # dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
210 |
211 | # ┌─────────────────────┐
212 | #───┘ Other configuration └─────────────────────────────────────
213 |
214 | # Whether disable HSTS
215 | #disableHsts: true
216 |
217 | # Number of worker processes
218 | #clusterLimit: 1
219 |
220 | # Job concurrency per worker
221 | # deliverJobConcurrency: 128
222 | # inboxJobConcurrency: 16
223 | # relationshipJobConcurrency: 16
224 | # What's relationshipJob?:
225 | # Follow, unfollow, block and unblock(ings) while following-imports, etc. or account migrations.
226 |
227 | # Job rate limiter
228 | # deliverJobPerSec: 128
229 | # inboxJobPerSec: 32
230 | # relationshipJobPerSec: 64
231 |
232 | # Job attempts
233 | # deliverJobMaxAttempts: 12
234 | # inboxJobMaxAttempts: 8
235 |
236 | # Local address used for outgoing requests
237 | #outgoingAddress: 127.0.0.1
238 |
239 | # IP address family used for outgoing request (ipv4, ipv6 or dual)
240 | #outgoingAddressFamily: ipv4
241 |
242 | # Amount of characters that can be used when writing notes (maximum: 8192, minimum: 1)
243 | maxNoteLength: 3000
244 |
245 | # Proxy for HTTP/HTTPS
246 | #proxy: http://127.0.0.1:3128
247 |
248 | proxyBypassHosts:
249 | - api.deepl.com
250 | - api-free.deepl.com
251 | - www.recaptcha.net
252 | - hcaptcha.com
253 | - challenges.cloudflare.com
254 |
255 | # Proxy for SMTP/SMTPS
256 | #proxySmtp: http://127.0.0.1:3128 # use HTTP/1.1 CONNECT
257 | #proxySmtp: socks4://127.0.0.1:1080 # use SOCKS4
258 | #proxySmtp: socks5://127.0.0.1:1080 # use SOCKS5
259 |
260 | # Media Proxy
261 | #mediaProxy: https://example.com/proxy
262 |
263 | # Proxy remote files (default: true)
264 | # Proxy remote files by this instance or mediaProxy to prevent remote files from running in remote domains.
265 | proxyRemoteFiles: true
266 |
267 | # Movie Thumbnail Generation URL
268 | # There is no reference implementation.
269 | # For example, Misskey will point to the following URL:
270 | # https://example.com/thumbnail.webp?thumbnail=1&url=https%3A%2F%2Fstorage.example.com%2Fpath%2Fto%2Fvideo.mp4
271 | #videoThumbnailGenerator: https://example.com
272 |
273 | # Sign to ActivityPub GET request (default: true)
274 | signToActivityPubGet: true
275 | # check that inbound ActivityPub GET requests are signed ("authorized fetch")
276 | checkActivityPubGetSignature: true
277 |
278 | # For security reasons, uploading attachments from the intranet is prohibited,
279 | # but exceptions can be made from the following settings. Default value is "undefined".
280 | # Read changelog to learn more (Improvements of 12.90.0 (2021/09/04)).
281 | #allowedPrivateNetworks: [
282 | # '127.0.0.1/32'
283 | #]
284 |
285 | #customMOTD: ['Hello World', 'The sharks rule all', 'Shonks']
286 |
287 | # Upload or download file size limits (bytes)
288 | #maxFileSize: 262144000
289 |
--------------------------------------------------------------------------------
/volumes/sharkey/docker.env:
--------------------------------------------------------------------------------
1 | # db settings
2 | POSTGRES_PASSWORD=example-misskey-pass
3 | POSTGRES_USER=example-misskey-user
4 | POSTGRES_DB=misskey
5 | DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@sharkey_db:5432/${POSTGRES_DB}"
6 |
--------------------------------------------------------------------------------