Compare commits

..

5 commits

Author SHA1 Message Date
tsmethurst 30ff677d92 update envparsing test 2023-01-06 14:02:43 +01:00
tsmethurst d3738e2720 report uri / regex functions 2023-01-06 13:50:32 +01:00
tsmethurst ea1efc31a5 implement report database functions 2023-01-06 13:50:22 +01:00
tsmethurst c1854a7dcc implement report cache + config changes 2023-01-06 13:49:24 +01:00
tsmethurst 838dfbead6 implement report database model 2023-01-06 13:48:52 +01:00
3886 changed files with 360430 additions and 718491 deletions

View file

@ -12,7 +12,7 @@ steps:
# We use golangci-lint for linting.
# See: https://golangci-lint.run/
- name: lint
image: golangci/golangci-lint:v1.53.1
image: golangci/golangci-lint:v1.50.1
volumes:
- name: go-build-cache
path: /root/.cache/go-build
@ -28,7 +28,7 @@ steps:
- pull_request
- name: test
image: golang:1.20.4-alpine
image: golang:1.19.3-alpine
volumes:
- name: go-build-cache
path: /root/.cache/go-build
@ -44,7 +44,7 @@ steps:
- pull_request
- name: web-setup
image: node:18-alpine
image: node:14-alpine
when:
event:
include:
@ -57,7 +57,7 @@ steps:
- yarn --frozen-lockfile --cache-folder /tmp/cache
- name: web-lint
image: node:18-alpine
image: node:14-alpine
when:
event:
include:
@ -69,7 +69,7 @@ steps:
- yarn run lint
- name: web-build
image: node:18-alpine
image: node:14-alpine
when:
event:
include:
@ -81,7 +81,7 @@ steps:
- yarn run build
- name: snapshot
image: superseriousbusiness/gotosocial-drone-build:0.2.0 # https://github.com/superseriousbusiness/gotosocial-drone-build
image: superseriousbusiness/gotosocial-drone-build:0.0.7 # https://github.com/superseriousbusiness/gotosocial-drone-build
volumes:
- name: go-build-cache
path: /root/.cache/go-build
@ -110,7 +110,7 @@ steps:
- main
- name: release
image: superseriousbusiness/gotosocial-drone-build:0.2.0 # https://github.com/superseriousbusiness/gotosocial-drone-build
image: superseriousbusiness/gotosocial-drone-build:0.0.7 # https://github.com/superseriousbusiness/gotosocial-drone-build
volumes:
- name: go-build-cache
path: /root/.cache/go-build
@ -169,7 +169,7 @@ clone:
steps:
- name: mirror
image: superseriousbusiness/gotosocial-drone-build:0.2.0
image: superseriousbusiness/gotosocial-drone-build:0.0.7
environment:
ORIGIN_REPO: https://github.com/superseriousbusiness/gotosocial
TARGET_REPO: https://codeberg.org/superseriousbusiness/gotosocial
@ -182,6 +182,6 @@ steps:
---
kind: signature
hmac: 946c2ffd4e79de07a767ec06ebac0a8ca70a03ce5666aae093c9b0af455041d1
hmac: 46dd4ce5b5dab76bc64c68f4730663ed0ada81471ffcbfa1bcf935d8e7f6a155
...

1
.gitattributes vendored
View file

@ -1 +0,0 @@
/vendor/ linguist-generated

View file

@ -1,67 +0,0 @@
name: Frontend Bug Report
description: Report an issue related to the web frontend
title: "[bug] Issue Title"
labels: ["bug", "frontend"]
assignees: []
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report! Please be cautious with the sensitive information/logs while filing the issue.
- type: textarea
id: desc
attributes:
label: Describe the bug with a clear and concise description of what the bug is. Please include screenshots of any visual issues.
validations:
required: true
- type: input
id: GoToSocial-Version
attributes:
label: What's your GoToSocial Version?
description: Enter the Version of your GoToSocial Installation
placeholder: e.g. v0.3.4
validations:
required: true
- type: input
id: Browser-Version
attributes:
label: Browser version
description: What browser(s) and versions are you using that show this bug?
placeholder: Firefox 103.0b9 (64-bit)
validations:
required: true
- type: textarea
id: what-happened
attributes:
label: What happened?
description: Enter exactly what happened.
validations:
required: false
- type: textarea
id: what-expected
attributes:
label: What you expected to happen?
description: Enter what you expected to happen.
validations:
required: false
- type: textarea
id: how-to-reproduce
attributes:
label: How to reproduce it?
description: As minimally and precisely as possible.
validations:
required: false
- type: textarea
id: anything-else
attributes:
label: Anything else we need to know?
validations:
required: false

View file

@ -2,23 +2,13 @@ name: Bug Report
description: Create a report to help us improve
title: "[bug] Issue Title"
labels: [bug]
assignees: []
assignees:
-
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report! Please be cautious with the sensitive information/logs while filing the issue.
- type: markdown
attributes:
value: |
⚠️ If you're reporting an interoperability issue with a **closed source**
ActivityPub/Mastodon client or server, please report the issue to the
developers of that client or server instead. Without access to their
source debugging the issue is difficult for us. Since GoToSocial is
open source, developers of closed source implementations can run a copy
and autonomously debug the issue. We'll gladly address any bugs they
raise with us.
- type: textarea
id: desc
attributes:
@ -44,6 +34,15 @@ body:
validations:
required: false
- type: input
id: Browser-Version
attributes:
label: Browser version
description: If this is a frontend bug, what browser(s) and versions are you using that show this bug?
placeholder: Firefox 103.0b9 (64-bit)
validations:
required: false
- type: textarea
id: what-happened
attributes:

3
.gitignore vendored
View file

@ -36,6 +36,3 @@ shell.nix
# ignore config dirs from IDEs
/.idea/
/.fleet/
# ignore cache dir from mkdocs serve
/.cache

View file

@ -14,74 +14,16 @@ run:
linters:
# enable some extra linters, see here for the list: https://golangci-lint.run/usage/linters/
enable:
- contextcheck
- forcetypeassert
- goconst
- gocritic
- gofmt
- goheader
- gosec
- nilerr
- revive
# https://golangci-lint.run/usage/linters/#linters-configuration
linters-settings:
# https://golangci-lint.run/usage/linters/#goheader
goheader:
template: |-
GoToSocial
Copyright (C) GoToSocial Authors admin@gotosocial.org
SPDX-License-Identifier: AGPL-3.0-or-later
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
# https://golangci-lint.run/usage/linters/#govet
govet:
disable:
- composites
# https://golangci-lint.run/usage/linters/#revive
revive:
rules:
# Enable most default rules.
# See: https://github.com/mgechev/revive/blob/master/defaults.toml
- name: blank-imports
- name: context-as-argument
- name: context-keys-type
- name: dot-imports
- name: error-naming
- name: error-return
- name: error-strings
- name: exported
- name: if-return
- name: increment-decrement
- name: var-naming
- name: var-declaration
- name: package-comments
- name: range
- name: receiver-naming
- name: time-naming
- name: unexported-return
- name: indent-error-flow
- name: errorf
- name: empty-block
- name: superfluous-else
- name: unreachable-code
# Disable below rules.
- name: redefines-builtin-id
disabled: true # This one is just annoying.
- name: unused-parameter
disabled: true # We often pass parameters to fulfil interfaces.
# https://golangci-lint.run/usage/linters/#staticcheck
staticcheck:
# Enable all checks, but disable SA1012: nil context passing.
# See: https://staticcheck.io/docs/configuration/options/#checks
checks: ["all", "-SA1012"]

View file

@ -24,8 +24,6 @@ builds:
- netgo
- osusergo
- static_build
- kvformat
- timetzdata
env:
- CGO_ENABLED=0
goos:

View file

@ -5,13 +5,9 @@
# Required
version: 2
build:
os: "ubuntu-20.04"
tools:
python: "mambaforge-4.10" # https://docs.readthedocs.io/en/stable/guides/conda.html#making-builds-faster-with-mamba
mkdocs:
configuration: "mkdocs.yml"
configuration: mkdocs.yml
conda:
environment: "docs/environment.yml"
python:
install:
- requirements: docs/requirements.txt

8
.vscode/launch.json vendored
View file

@ -2,18 +2,14 @@
"version": "0.2.0",
"configurations": [
{
"name": "Debug Package",
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "debug",
"mode": "auto",
"program": "${workspaceFolder}/cmd/gotosocial",
"args": [
"testrig", "start"
],
"buildFlags": "-tags='netgo osusergo static_build kvformat debugenv'",
"env": {
"DEBUG": "1",
},
"cwd": "${workspaceFolder}"
}
]

View file

@ -2,11 +2,5 @@
"go.lintTool":"golangci-lint",
"go.lintFlags": [
"--fast"
],
"go.vetFlags": [
"-composites=false ."
],
"eslint.workingDirectories": ["web/source"],
"eslint.lintTask.enable": true,
"eslint.lintTask.options": "${workspaceFolder}/web/source"
]
}

View file

@ -24,12 +24,11 @@ These contribution guidelines were adapted from / inspired by those of Gitea (ht
- [Finding your way around the code](#finding-your-way-around-the-code)
- [Style / Linting / Formatting](#style--linting--formatting)
- [Testing](#testing)
- [Standalone Testrig with Semaphore](#standalone-testrig-with-semaphore)
- [Standalone Testrig with Pinafore](#standalone-testrig-with-pinafore)
- [Running automated tests](#running-automated-tests)
- [SQLite](#sqlite)
- [Postgres](#postgres)
- [CLI Tests](#cli-tests)
- [Federation](#federation)
- [Updating Swagger docs](#updating-swagger-docs)
- [CI/CD configuration](#cicd-configuration)
- [Release Checklist](#release-checklist)
@ -97,19 +96,6 @@ The process for documentation pull requests is a bit looser than the process for
If you see something in the documentation that's missing, wrong, or unclear, feel free to open a pull request addressing it; you don't necessarily need to open an issue first, but please explain why you're opening the PR in the PR comment.
We support a [Conda](https://docs.conda.io/en/latest/)-based workflow for hacking, building & publishing the documentation. Here's how you can get started locally:
* Install [`miniconda`](https://docs.conda.io/en/latest/miniconda.html)
* Create your conda environment: `conda env create -f ./docs/environment.yml`
* Activate the environment: `conda activate gotosocial-docs`
* Serve locally: `mkdocs serve`
Then you can visit [localhost:8000](http://127.0.0.1:8000/) in your browser to view.
When adding a new page, you need to include it in the [`mkdocs.yml`](mkdocs.yml) so it shows in the sidebar in the right section.
If you don't use Conda, you can read the `docs/environment.yml` to see which dependencies are required and `pip install` them manually. It's advisable to do this in a virtual environment, which you can create with something like `python3 -m venv /path-to/store-the-venv`. You can then call `/path-to/store-the-venv/bin/pip`, `/path-to/store-the-venv/bin/mkdocs` etc.
## Development
### Golang forking quirks
@ -148,7 +134,7 @@ In case this post disappears, here are the steps (slightly modified):
#### Binary
To get started, you first need to have Go installed. GtS is currently using Go 1.20, so you should take that too. See [here](https://golang.org/doc/install) for installation instructions.
To get started, you first need to have Go installed. GtS is currently using Go 1.19, so you should take that too. See [here](https://golang.org/doc/install) for installation instructions. **WARNING: Go version 1.19.4 does not work due to a bug in Go. Use 1.19.3 or 1.19.5+** instead.
Once you've got go installed, clone this repository into your Go path. Normally, this should be `~/go/src/github.com/superseriousbusiness/gotosocial`.
@ -224,8 +210,6 @@ GoToSocial uses Gin templates in the `web/template` folder. Static assets are st
To bundle changes, you need [Node.js](https://nodejs.org/en/download/) and [Yarn](https://classic.yarnpkg.com/en/docs/install).
Using [NVM](https://github.com/nvm-sh/nvm) is one convenient way to install them which also supports managing different Node versions.
To install Yarn dependencies:
```bash
@ -374,26 +358,25 @@ GoToSocial provides a [testrig](https://github.com/superseriousbusiness/gotosoci
One thing that *isn't* mocked is the Database interface because it's just easier to use an in-memory SQLite database than to mock everything out.
#### Standalone Testrig with Semaphore
#### Standalone Testrig with Pinafore
You can launch a testrig as a standalone server running at localhost, which you can connect to using something like [Semaphore](https://github.com/NickColley/semaphore/).
You can launch a testrig as a standalone server running at localhost, which you can connect to using something like [Pinafore](https://github.com/nolanlawson/pinafore).
To do this, first build the gotosocial binary with `DEBUG=1 ./scripts/build.sh`.
To do this, first build the gotosocial binary with `./scripts/build.sh`.
Then, launch the testrig with the `DEBUG` environment variable set by invoking the binary as follows:
Then, launch the testrig by invoking the binary as follows:
```bash
DEBUG=1 ./gotosocial testrig start
./gotosocial testrig start
```
To run Semaphore locally in dev mode, first clone the [Semaphore](https://github.com/NickColley/semaphore/) repository, and then run the following commands in the cloned directory:
To run Pinafore locally in dev mode, first clone the [Pinafore](https://github.com/nolanlawson/pinafore) repository, and then run the following command in the cloned directory:
```bash
yarn # install dependencies
yarn run dev
```
The Semaphore instance will start running on `localhost:4002`.
The Pinafore instance will start running on `localhost:4002`.
To connect to the testrig, navigate to `http://localhost:4002` and enter your instance name as `localhost:8080`.
@ -435,24 +418,6 @@ In [./test/envparsing.sh](./test/envparsing.sh) there's a test for making sure t
Although this test *is* part of the CI/CD testing process, you probably won't need to worry too much about running it yourself. That is, unless you're messing about with code inside the `main` package in `cmd/gotosocial`, or inside the `config` package in `internal/config`.
#### Federation
By using the support for loading TLS files from disk it is possible to have two or more local instances with TLS to allow for (manually) testing federation.
You'll need to set the following configuration options:
- `GTS_TLS_CERTIFICATE_CHAIN`: poiting to a PEM-encoded certificate chain including the public certificate.
- `GTS_TLS_CERTIFICATE_KEY`: pointing to a PEM-encoded private key.
Additionally, for the Go HTTP client to recognise certificates issued by a custom CA as valid, you'll need to set one of:
- `SSL_CERT_FILE`: pointing to the public key of your custom CA.
- `SSL_CERT_DIR`: a `:`-separated list of directories to load CA certificates from.
The above `SSL_CERT` variables work on Unix-like systems only, excluding Mac. See https://pkg.go.dev/crypto/x509#SystemCertPool. If you are running your tests on an architecture that doesn't support setting the above variables, you can instead disable TLS certificate verification for the HTTP client entirely by setting `http-client.tls-insecure-skip-verify` to `true` in the config.yaml file.
You'll additionally need functioning DNS for your two instance names, which you can achieve through entries in `/etc/hosts` or by running a local DNS server like [dnsmasq](https://thekelleys.org.uk/dnsmasq/doc.html).
### Updating Swagger docs
GoToSocial uses [go-swagger](https://goswagger.io) to generate Swagger API documentation from code annotations.

View file

@ -2,7 +2,7 @@
# Dockerfile reference: https://docs.docker.com/engine/reference/builder/
# stage 1: generate up-to-date swagger.yaml to put in the final container
FROM --platform=${BUILDPLATFORM} quay.io/goswagger/swagger:v0.30.4 AS swagger
FROM --platform=${BUILDPLATFORM} quay.io/goswagger/swagger:v0.30.0 AS swagger
COPY go.mod /go/src/github.com/superseriousbusiness/gotosocial/go.mod
COPY go.sum /go/src/github.com/superseriousbusiness/gotosocial/go.sum
@ -12,7 +12,7 @@ WORKDIR /go/src/github.com/superseriousbusiness/gotosocial
RUN swagger generate spec -o /go/src/github.com/superseriousbusiness/gotosocial/swagger.yaml --scan-models
# stage 2: generate the web/assets/dist bundles
FROM --platform=${BUILDPLATFORM} node:16.19.1-alpine3.17 AS bundler
FROM --platform=${BUILDPLATFORM} node:16.15.1-alpine3.15 AS bundler
COPY web web
RUN yarn install --cwd web/source && \
@ -20,7 +20,7 @@ RUN yarn install --cwd web/source && \
rm -r web/source
# stage 3: build the executor container
FROM --platform=${TARGETPLATFORM} alpine:3.17.2 as executor
FROM --platform=${TARGETPLATFORM} alpine:3.15.4 as executor
# switch to non-root user:group for GtS
USER 1000:1000

148
README.md
View file

@ -8,7 +8,7 @@ With GoToSocial, you can keep in touch with your friends, post, read, and share
<img src="./docs/assets/sloth.png" width="300"/>
</p>
**GoToSocial is still [ALPHA SOFTWARE](https://en.wikipedia.org/wiki/Software_release_life_cycle#Alpha)**. It is already deployable and useable, and it federates cleanly with many other Fediverse servers (not yet all). However, many things are not yet implemented, and there are plenty of bugs! We foresee entering beta around the beginning of 2024.
**GoToSocial is still [ALPHA SOFTWARE](https://en.wikipedia.org/wiki/Software_release_life_cycle#Alpha)**. It is already deployable and useable, and it federates cleanly with many other Fediverse servers (not yet all). However, many things are not yet implemented, and there are plenty of bugs! We foresee entering beta somewhere in 2023.
Documentation is at [docs.gotosocial.org](https://docs.gotosocial.org). You can skip straight to the API documentation [here](https://docs.gotosocial.org/en/latest/api/swagger/). To build from source, check the [CONTRIBUTING.md](./CONTRIBUTING.md) file.
@ -33,8 +33,6 @@ Here's a screenshot of the instance landing page!
- [Wishlist](#wishlist)
- [Getting Started](#getting-started)
- [Third-Party Packaging](#third-party-packaging)
- [Distribution packaging](#distribution-packaging)
- [Self-hosting](#self-hosting)
- [Known Issues](#known-issues)
- [Client App Issues](#client-app-issues)
- [Federation Issues](#federation-issues)
@ -43,12 +41,13 @@ Here's a screenshot of the instance landing page!
- [Contact](#contact)
- [Credits](#credits)
- [Libraries](#libraries)
- [Image Attribution and Licensing](#image-attribution-and-licensing)
- [Image Attribution](#image-attribution)
- [Team](#team)
- [Special Thanks](#special-thanks)
- [Sponsorship + Funding](#sponsorship--funding)
- [Crowdfunding](#crowdfunding)
- [NLnet](#nlnet)
- [OpenCollective](#opencollective)
- [LiberaPay](#liberapay)
- [Provisos](#provisos)
- [License](#license)
## What is GoToSocial?
@ -91,13 +90,13 @@ For a detailed view on what's implemented and what's not, and progress made towa
The Mastodon API has become the de facto standard for client communication with federated servers, so GoToSocial has implemented and extended the API with custom functionality.
In short, this means full support for modern, beautiful apps like [Tusky](https://tusky.app/) and [Semaphore](https://semaphore.social/).
In short, this means full support for modern, beautiful apps like [Tusky](https://tusky.app/) and [Pinafore](https://pinafore.social/).
Tusky | Semaphore
Tusky | Pinafore
:-----------------------------------------------------------:|:------------------------------------------------------------------:
![An image of GoToSocial in Tusky](./docs/assets/tusky.png) | ![An image of GoToSocial in Semaphore](./docs/assets/semaphore.png)
![An image of GoToSocial in Tusky](./docs/assets/tusky.png) | ![An image of GoToSocial in Pinafore](./docs/assets/pinafore.png)
If you're used to using Mastodon with Tusky or Semaphore, you'll find using GoToSocial a breeze.
If you're used to using Mastodon with Tusky or Pinafore, you'll find using GoToSocial a breeze.
### Granular post settings
@ -137,8 +136,8 @@ GoToSocial plays nice with lower-powered machines like Raspberry Pi, old laptops
GoToSocial doesn't apply a one-size-fits-all approach to federation. Who your server federates with should be up to you.
- 'Normal' federation; discover new servers.
- *Allow list*-only federation; choose which servers you talk to (not yet implemented).
- Zero federation; keep your server private (not yet implemented).
- *Allow list*-only federation; choose which servers you talk to.
- Zero federation; keep your server private.
### OIDC integration
@ -171,22 +170,13 @@ All docs for installation and configuration are hosted at [docs.gotosocial.org](
## Third-Party Packaging
Thank you so much to the cool people who have put time and energy into packaging GoToSocial!
### Distribution packaging
These packages are not maintained by GoToSocial, so please direct questions and issues to the repository maintainers (and donate to them!).
[![Packaging status](https://repology.org/badge/vertical-allrepos/gotosocial.svg)](https://repology.org/project/gotosocial/versions)
### Self-hosting
You can deploy your own instance of GoToSocial with the help of:
Thank you so much to the cool people who have put time and energy into packaging GoToSocial! Known third-party packaging projects are listed below:
- [YunoHost GoToSocial Packaging](https://github.com/YunoHost-Apps/gotosocial_ynh) by [OniriCorpe](https://github.com/OniriCorpe).
- [Ansible Playbook (MASH)](https://github.com/mother-of-all-self-hosting/mash-playbook): The playbook supports a many services, including GoToSocial. [Documentation](https://github.com/mother-of-all-self-hosting/mash-playbook/blob/main/docs/services/gotosocial.md)
- GoToSocial Helm Charts:
- [GoToSocial Helm Chart](https://github.com/fSocietySocial/charts/tree/main/charts/gotosocial) by [0hlov3](https://github.com/0hlov3).
- [GoToSocial Helm Chart](https://github.com/Maxxblow/charts/tree/main/charts/gotosocial) by [0hlov3](https://github.com/0hlov3).
These packages are not maintained by GoToSocial, so please direct questions and issues to the repository maintainers (and donate to them!).
## Known Issues
@ -194,7 +184,7 @@ Since GoToSocial is still in alpha, there are plenty of bugs. We use [GitHub iss
### Client App Issues
GoToSocial works great with Tusky and Semaphore, but some other client applications still need work or have issues connecting to GoToSocial. We're tracking them [right here](https://github.com/superseriousbusiness/gotosocial/projects/5). It's our goal to make any app that's compatible with the Mastodon API work seamlessly with GoToSocial.
GoToSocial works great with Tusky and Pinafore, but some other client applications still need work or have issues connecting to GoToSocial. We're tracking them [right here](https://github.com/superseriousbusiness/gotosocial/projects/5). It's our goal to make any app that's compatible with the Mastodon API work seamlessly with GoToSocial.
### Federation Issues
@ -218,84 +208,60 @@ For bugs and feature requests, please check to see if there's [already an issue]
### Libraries
The following open source libraries, frameworks, and tools are used by GoToSocial, with gratitude 💕
The following libraries and frameworks are used by GoToSocial, with gratitude 💕
- [abema/go-mp4](https://github.com/abema/go-mp4); mp4 parsing. [MIT License](https://spdx.org/licenses/MIT.html).
- [buckket/go-blurhash](https://github.com/buckket/go-blurhash); used for generating image blurhashes. [GPL-3.0 License](https://spdx.org/licenses/GPL-3.0-only.html).
- [coreos/go-oidc](https://github.com/coreos/go-oidc); OIDC client library. [Apache-2.0 License](https://spdx.org/licenses/Apache-2.0.html).
- [disintegration/imaging](https://github.com/disintegration/imaging); image resizing. [MIT License](https://spdx.org/licenses/MIT.html).
- [DmitriyVTitov/size](https://github.com/DmitriyVTitov/size); runtime model memory size calculations. [MIT License](https://spdx.org/licenses/MIT.html).
- Gin:
- [gin-gonic/gin](https://github.com/gin-gonic/gin); speedy router engine. [MIT License](https://spdx.org/licenses/MIT.html).
- [gin-contrib/cors](https://github.com/gin-contrib/cors); Gin CORS middleware. [MIT License](https://spdx.org/licenses/MIT.html).
- [gin-contrib/gzip](https://github.com/gin-contrib/gzip); Gin gzip middleware. [MIT License](https://spdx.org/licenses/MIT.html).
- [gin-contrib/sessions](https://github.com/gin-contrib/sessions); Gin sessions middleware. [MIT License](https://spdx.org/licenses/MIT.html).
- [gin-gonic/gin](https://github.com/gin-gonic/gin); speedy router engine. [MIT License](https://spdx.org/licenses/MIT.html).
- [gin-contrib/static](https://github.com/gin-contrib/static); Gin static page middleware. [MIT License](https://spdx.org/licenses/MIT.html).
- [go-fed/httpsig](https://github.com/go-fed/httpsig); secure HTTP signature library. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html).
- [google/uuid](https://github.com/google/uuid); UUID generation. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html).
- [google/wuffs](https://github.com/google/wuffs); png-stripping code. [Apache-2.0 License](https://spdx.org/licenses/Apache-2.0.html).
- Go-Playground:
- [go-playground/form](https://github.com/go-playground/form); funky form mapping support. [MIT License](https://spdx.org/licenses/MIT.html).
- [go-playground/validator](https://github.com/go-playground/validator); struct validation. [MIT License](https://spdx.org/licenses/MIT.html).
- Gorilla:
- [gorilla/feeds](https://github.com/gorilla/feeds); RSS + Atom feed generation. [BSD-2-Clause License](https://spdx.org/licenses/BSD-2-Clause.html).
- [gorilla/websocket](https://github.com/gorilla/websocket); Websocket connectivity. [BSD-2-Clause License](https://spdx.org/licenses/BSD-2-Clause.html).
- [go-swagger/go-swagger](https://github.com/go-swagger/go-swagger); Swagger OpenAPI spec generation. [Apache-2.0 License](https://spdx.org/licenses/Apache-2.0.html).
- gruf:
- [gruf/go-bytesize](https://codeberg.org/gruf/go-bytesize); byte size parsing / formatting. [MIT License](https://spdx.org/licenses/MIT.html).
- [gruf/go-cache](https://codeberg.org/gruf/go-cache); object & result caching. [MIT License](https://spdx.org/licenses/MIT.html).
- [gruf/go-debug](https://codeberg.org/gruf/go-debug); debug build tag. [MIT License](https://spdx.org/licenses/MIT.html).
- [gruf/go-errors](https://codeberg.org/gruf/go-errors); performant multi-error checking [MIT License](https://spdx.org/licenses/MIT.html).
- [gruf/go-fastcopy](https://codeberg.org/gruf/go-fastcopy); performant pooled I/O copying [MIT License](https://spdx.org/licenses/MIT.html).
- [gruf/go-kv](https://codeberg.org/gruf/go-kv); log field formatting. [MIT License](https://spdx.org/licenses/MIT.html).
- [gruf/go-mutexes](https://codeberg.org/gruf/go-mutexes); safemutex & mutex map. [MIT License](https://spdx.org/licenses/MIT.html).
- [gruf/go-runners](https://codeberg.org/gruf/go-runners); workerpools and synchronization. [MIT License](https://spdx.org/licenses/MIT.html).
- [gruf/go-sched](https://codeberg.org/gruf/go-sched); task scheduler. [MIT License](https://spdx.org/licenses/MIT.html).
- [gruf/go-store](https://codeberg.org/gruf/go-store); file storage backend (local & s3). [MIT License](https://spdx.org/licenses/MIT.html).
- [go-playground/validator](https://github.com/go-playground/validator); struct validation. [MIT License](https://spdx.org/licenses/MIT.html).
- [gorilla/feeds](https://github.com/gorilla/feeds); RSS + Atom feed generation. [BSD-2-Clause License](https://spdx.org/licenses/BSD-2-Clause.html).
- [gorilla/websocket](https://github.com/gorilla/websocket); Websocket connectivity. [BSD-2-Clause License](https://spdx.org/licenses/BSD-2-Clause.html).
- [gruf/go-debug](https://codeberg.org/gruf/go-debug); profiling support in debug builds. [MIT License](https://spdx.org/licenses/MIT.html).
- [gruf/go-bytesize](https://codeberg.org/gruf/go-bytesize); byte size parsing / formatting. [MIT License](https://spdx.org/licenses/MIT.html).
- [gruf/go-cache](https://codeberg.org/gruf/go-cache); object caching. [MIT License](https://spdx.org/licenses/MIT.html).
- [gruf/go-kv](https://codeberg.org/gruf/go-kv); key-value field formatting. [MIT License](https://spdx.org/licenses/MIT.html).
- [gruf/go-mutexes](https://codeberg.org/gruf/go-mutexes); mutex map. [MIT License](https://spdx.org/licenses/MIT.html).
- [gruf/go-runners](https://codeberg.org/gruf/go-runners); worker pool library. [MIT License](https://spdx.org/licenses/MIT.html).
- [gruf/go-store](https://codeberg.org/gruf/go-store); local media store. [MIT License](https://spdx.org/licenses/MIT.html).
- [h2non/filetype](https://github.com/h2non/filetype); filetype checking. [MIT License](https://spdx.org/licenses/MIT.html).
- jackc:
- [jackc/pgx](https://github.com/jackc/pgconn); Postgres driver. [MIT License](https://spdx.org/licenses/MIT.html).
- [jackc/pgx](https://github.com/jackc/pgx); Postgres driver and toolkit. [MIT License](https://spdx.org/licenses/MIT.html).
- [KimMachineGun/automemlimit](https://github.com/KimMachineGun/automemlimit); cgroups memory limit checking. [MIT License](https://spdx.org/licenses/MIT.html).
- [jackc/pgx](https://github.com/jackc/pgx); Postgres driver. [MIT License](https://spdx.org/licenses/MIT.html).
- [mcuadros/go-syslog](https://github.com/mcuadros/go-syslog); Syslog server library. [MIT License](https://spdx.org/licenses/MIT.html).
- [microcosm-cc/bluemonday](https://github.com/microcosm-cc/bluemonday); HTML user-input sanitization. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html).
- [miekg/dns](https://github.com/miekg/dns); DNS utilities. [Go License](https://go.dev/LICENSE).
- [minio/minio-go](https://github.com/minio/minio-go); S3 client SDK. [Apache-2.0 License](https://spdx.org/licenses/Apache-2.0.html).
- [mitchellh/mapstructure](https://github.com/mitchellh/mapstructure); Go interface => struct parsing. [MIT License](https://spdx.org/licenses/MIT.html).
- [modernc.org/sqlite](https://gitlab.com/cznic/sqlite); cgo-free port of SQLite. [Other License](https://gitlab.com/cznic/sqlite/-/blob/master/LICENSE).
- [mvdan.cc/xurls](https://github.com/mvdan/xurls); URL parsing regular expressions. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html).
- [modernc.org/ccgo](https://gitlab.com/cznic/ccgo); c99 AST -> Go translater. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html).
- [modernc.org/libc](https://gitlab.com/cznic/libc); C-runtime services. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html).
- [mvdan/xurls](https://github.com/mvdan/xurls); URL parsing regular expressions. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html).
- [oklog/ulid](https://github.com/oklog/ulid); sequential, database-friendly ID generation. [Apache-2.0 License](https://spdx.org/licenses/Apache-2.0.html).
- [open-telemetry/opentelemetry-go](https://github.com/open-telemetry/opentelemetry-go); OpenTelemetry API + SDK. [Apache-2.0 License](https://spdx.org/licenses/Apache-2.0.html).
- spf13:
- [spf13/cobra](https://github.com/spf13/cobra); command-line tooling. [Apache-2.0 License](https://spdx.org/licenses/Apache-2.0.html).
- [spf13/viper](https://github.com/spf13/viper); configuration management. [Apache-2.0 License](https://spdx.org/licenses/Apache-2.0.html).
- [ReneKroon/ttlcache](https://github.com/ReneKroon/ttlcache); in-memory caching. [MIT License](https://spdx.org/licenses/MIT.html).
- [robfig/cron](https://github.com/robfig/cron); cron job scheduling. [MIT License](https://spdx.org/licenses/MIT.html).
- [russross/blackfriday](https://github.com/russross/blackfriday); markdown parsing for statuses. [Simplified BSD License](https://spdx.org/licenses/BSD-2-Clause.html).
- [spf13/cobra](https://github.com/spf13/cobra); command-line tooling. [Apache-2.0 License](https://spdx.org/licenses/Apache-2.0.html).
- [spf13/pflag](https://github.com/spf13/pflag); command-line flag utilities. [Apache-2.0 License](https://spdx.org/licenses/Apache-2.0.html).
- [spf13/viper](https://github.com/spf13/viper); configuration management. [Apache-2.0 License](https://spdx.org/licenses/Apache-2.0.html).
- [stretchr/testify](https://github.com/stretchr/testify); test framework. [MIT License](https://spdx.org/licenses/MIT.html).
- superseriousbusiness:
- [superseriousbusiness/activity](https://github.com/superseriousbusiness/activity) forked from [go-fed/activity](https://github.com/go-fed/activity); Golang ActivityPub/ActivityStreams library. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html).
- [superseriousbusiness/exif-terminator](https://github.com/superseriousbusiness/exif-terminator); EXIF data removal. [GNU AGPL v3 LICENSE](https://spdx.org/licenses/AGPL-3.0-or-later.html).
- [superseriousbusiness/oauth2](https://github.com/superseriousbusiness/oauth2) forked from [go-oauth2/oauth2](https://github.com/go-oauth2/oauth2); OAuth server framework and token handling. [MIT License](https://spdx.org/licenses/MIT.html).
- [superseriousbusiness/exif-terminator](https://github.com/superseriousbusiness/exif-terminator); EXIF data removal. [GNU AGPL v3 LICENSE](https://spdx.org/licenses/AGPL-3.0-or-later.html).
- [superseriousbusiness/activity](https://github.com/superseriousbusiness/activity) forked from [go-fed/activity](https://github.com/go-fed/activity); Golang ActivityPub/ActivityStreams library. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html).
- [superseriousbusiness/oauth2](https://github.com/superseriousbusiness/oauth2) forked from [go-oauth2/oauth2](https://github.com/go-oauth2/oauth2); OAuth server framework and token handling. [MIT License](https://spdx.org/licenses/MIT.html).
- [go-swagger/go-swagger](https://github.com/go-swagger/go-swagger); Swagger OpenAPI spec generation. [Apache-2.0 License](https://spdx.org/licenses/Apache-2.0.html).
- [tdewolff/minify](https://github.com/tdewolff/minify); HTML minification for Markdown-submitted posts. [MIT License](https://spdx.org/licenses/MIT.html).
- [uber-go/automaxprocs](https://github.com/uber-go/automaxprocs); GOMAXPROCS automation. [MIT License](https://spdx.org/licenses/MIT.html).
- [ulule/limiter](https://github.com/ulule/limiter); http rate limit middleware. [MIT License](https://spdx.org/licenses/MIT.html).
- [uptrace/bun](https://github.com/uptrace/bun); database ORM. [BSD-2-Clause License](https://spdx.org/licenses/BSD-2-Clause.html).
- [wagslane/go-password-validator](https://github.com/wagslane/go-password-validator); password strength validation. [MIT License](https://spdx.org/licenses/MIT.html).
- [yuin/goldmark](https://github.com/yuin/goldmark); markdown parser. [MIT License](https://spdx.org/licenses/MIT.html).
- [ulule/limiter](https://github.com/ulule/limiter); http rate limit middleware. [MIT License](https://spdx.org/licenses/MIT.html).
### Image Attribution and Licensing
### Image Attribution
Sloth logo by [Anna Abramek](https://abramek.art/).
<a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png" /></a><br />The GoToSocial sloth mascot is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">Creative Commons Attribution-ShareAlike 4.0 International License</a>.
The Creative Commons Attribution-ShareAlike 4.0 International License license applies specifically to the following files and subdirectories of this repository:
- [sloth logo png](./web/assets/logo.png)
- [sloth logo svg](./web/assets/logo.svg)
- [all default avatars](./web/assets/default_avatars)
Under the terms of the license, you are free to:
- Share — copy and redistribute the abovementioned material in any medium or format.
- Adapt — remix, transform, and build upon the abovementioned material for any purpose, even commercially.
Sloth logo by [Anna Abramek](https://abramek.art/), Copyright (C) 2021-2023 the GoToSocial Authors.
### Team
@ -314,27 +280,23 @@ Thanks to everyone who has used GtS, opened an issue, suggested something, given
## Sponsorship + Funding
**Please note: GoToSocial has NO CORPORATE SPONSORS and does not desire corporate sponsorship. In addition, we do not take donations from any of the following: adult websites, affiliate and review websites, casinos and gambling, insurance and financial products (credit), pharmacy products, SEO services and social media buying, VPN and proxy services, and essay writing services. Donations from such sources will be automatically rejected.**
### Crowdfunding
### OpenCollective
![open collective Standard Sloth badge](https://opencollective.com/gotosocial/tiers/standard-sloth/badge.svg?label=Standard%20Sloth&color=brightgreen) ![open collective Stable Sloth badge](https://opencollective.com/gotosocial/tiers/stable-sloth/badge.svg?label=Stable%20Sloth&color=green) ![open collective Special Sloth badge](https://opencollective.com/gotosocial/tiers/special-sloth/badge.svg?label=Special%20Sloth&color=yellowgreen) ![open collective Sugar Sloth badge](https://opencollective.com/gotosocial/tiers/sugar-sloth/badge.svg?label=Sugar%20Sloth&color=blue)
If you would like to donate to GoToSocial to keep the lights on during development, [you can do so via our OpenCollective page](https://opencollective.com/gotosocial#support)!
Currently, work on GoToSocial is funded through donations to our [OpenCollective](https://opencollective.com/gotosocial) page.
If you would like to donate to GoToSocial to keep the lights on during development, [you can do so here](https://opencollective.com/gotosocial#support)! 💕 🦥 💕 Thank you!
### LiberaPay
![LiberaPay patrons](https://img.shields.io/liberapay/patrons/GoToSocial.svg?logo=liberapay) ![receives via LiberaPay](https://img.shields.io/liberapay/receives/GoToSocial.svg?logo=liberapay)
If you prefer, we also have an account on LiberaPay! You can find that [right here](https://liberapay.com/GoToSocial/).
Crowdfunded donations to our OpenCollective and Liberapay accounts go towards paying the core team, paying server costs, and paying for GtS art, design, and other bits and bobs.
### Provisos
💕 🦥 💕 Thank you!
### NLnet
<img src="https://nlnet.nl/logo/NGI/NGIZero-green.hex.svg" width="75" alt="NGIZero logo"/>
Combined with the above crowdfunding sources, 2023 Alpha development of GoToSocial is also funded by a 50,000 EUR grant from the [NGI0 Entrust Fund](https://nlnet.nl/entrust/), via [NLnet](https://nlnet.nl/). See [here](https://nlnet.nl/project/GoToSocial/#ack) for more details. The successful grant application is archived [here](https://github.com/superseriousbusiness/gotosocial/blob/main/archive/nlnet/2022-next-generation-internet-zero.md).
**Please note: GoToSocial has NO CORPORATE SPONSORS and does not desire corporate sponsorship. In addition, we do not take donations from any of the following: adult websites, affiliate and review websites, casinos and gambling, insurance and financial products (credit), pharmacy products, SEO services and social media buying, VPN and proxy services, and essay writing services. Donations from such sources will be automatically rejected.**
## License
@ -349,5 +311,3 @@ If you modify the GoToSocial source code, and run that modified code in a way th
> \[I\]f you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software.
Copyright (C) 2021-2023 GoToSocial Authors
(I'm adding this here to take the crown of having the 1000th commit ~ kim)

View file

@ -1,69 +1,291 @@
# Roadmap to Beta <!-- omit in toc -->
# Roadmap <!-- omit in toc -->
This document contains the roadmap for GoToSocial to be considered eligible for its first [beta release](https://en.wikipedia.org/wiki/Software_release_life_cycle#Beta).
This document contains the roadmap for GoToSocial to be considered eligible for its first [beta release](https://en.wikipedia.org/wiki/Software_release_life_cycle#Beta), and a summary of progress made towards that goal.
All the info contained in this document is best-guess only. It's useful to have a rough timeline we can direct people to, but things will undoubtedly change along the way; don't hold us to anything in this doc!
Thank you to [NLnet](https://nlnet.nl) for helping to fund the alpha phase of GoToSocial development and get us moving towards beta!
Big thank you to all of our [Open Collective](https://opencollective.com/gotosocial) and [Liberapay](https://liberapay.com/gotosocial) contributors, who've helped us keep the lights on! 💕
All the dates contained in this document are best-guess only. It's useful to have a rough timeline, but things will undoubtedly change along the way.
## Table of Contents <!-- omit in toc -->
- [Beta Aims](#beta-aims)
- [Timeline](#timeline)
- [Mid 2023](#mid-2023)
- [Mid/late 2023](#midlate-2023)
- [Late 2023](#late-2023)
- [Early 2024](#early-2024)
- [And then...](#and-then)
- [Q3 2022](#q3-2022)
- [Q4 2022](#q4-2022)
- [Q1 2023](#q1-2023)
- [Q2 2023](#q2-2023)
- [Q3 2023](#q3-2023)
- [Detailed To-do List](#detailed-to-do-list)
- [API Groups + Endpoints](#api-groups--endpoints)
- [Non-API tasks](#non-api-tasks)
## Beta Aims
Every software project has a different idea of what it means to be "in beta". In our case, we want the beta version of GoToSocial to provide a feature set that roughly compares to existing popular ActivityPub server implementations.
The milestone for beta in GoToSocial's case is to have a feature set that roughly compares to existing popular ActivityPub server implementations (minus features which we don't see as particularly important or useful). The beta milestone also includes GoToSocial-specific features which, we believe, are vital for user safety, such as local-only posting, block list subscriptions, and so on.
In other words, you should be able to use the beta version of GoToSocial as your go-to(!) fedi instance for following people and making posts, without running into too many issues with things being missing or not working properly.
Once feature parity is roughly in place, we will use beta time to start adding and polishing bonus features like slow federation, group moderation decisions, migration tooling, etc.
Our target for beta also includes features which, we believe, are vital for user safety and wellbeing, such as non-replyable posting, block list subscription, allow-list support, and so on.
Once we have the features we want in order to say "this is now a beta", we will use beta time to work on fixing bugs, tuning performance, and adding extra features which require a stable base to build on.
Our hope for beta is also that, once we're there, the client API will stay relatively stable, and folks can confidently start building applications on top of GoToSocial without worrying that the API will change overnight and necessitate major rewrites.
We currently foresee entering beta phase around the start of 2024, though this is only an estimate and is subject to change.
We currently foresee entering beta phase around the middle of 2023, though this is only an estimate and is subject to change.
## Timeline
What follows is a rough timeline of features that will be implemented on the road to beta. The timeline is calculated on the following assumptions:
What follows is a per-quarter timeline of features that will be implemented on the road to beta. The timeline is calculated on the following assumptions:
- We will continue to develop at a pace that is similar to what we've done over the previous two years.
- We will continue to develop at a pace that is similar to what we've done over the previous year.
- Our combined bandwidth is roughly equivalent to one person working full time on the project.
- One distinct 'feature' takes one person 2-4 weeks to develop and test, depending on the size of the feature.
- There will be other bugs to fix in between implementing things, so we shouldn't pack in features too tightly.
**This timeline is a best-guess about when things will be implemented. The order of feature releases is not fixed. Things may go faster or slower depending on the number of hurdles we run into, and the amount of help we receive from community contributions of code. The timeline also does not include background tasks like admin, polishing existing features, refactoring code, release management, and ensuring compatibility with other AP implementations.**
Each quarter contains one 'big feature' which will probably take the longest amount of time that quarter.
### Mid 2023
**This timeline is a best-guess about when things will be implemented. The order of feature releases may change. It may go faster or slower depending on the number of hurdles we run into, and the amount of help we receive from community contributions of code. The timeline also does not include background tasks like admin, polishing existing features, refactoring code, and ensuring compatibility with other AP implementations.**
- **Hashtags** -- implement federating hashtags and viewing hashtags to allow users to discover posts that they might be interested in.
- **Block list subscriptions** -- allow instance admins to subscribe their instance to plaintext domain block lists (much of the work for this is already in place).
- **Direct conversation view** -- allow users to easily page through all direct-message conversations they're a part of.
### Q3 2022
### Mid/late 2023
- **Big Feature** -- User settings page: allow users to edit their profile page and settings through a web page served by the GtS instance, for cases where client apps don't implement their own settings page.
- Block list subscription support: allow instance admins to subscribe their instance to plaintext domain block lists (much of the work for this is already in place).
- Tag support: implement federating hashtags and viewing hashtags to allow users to discover posts that they might be interested in.
- **Polls** -- implementing parsing, creating, and voting in polls.
- **Mute posts/threads** -- opt-out of notifications for replies to a thread; no longer show a given post in your timeline.
- **Limited peering/allowlists** -- allow instance admins to limit federation with other instances by default.
### Q4 2022
### Late 2023
- **Big Feature** — Video support: allow users to view + post videos (before we only implemented images and gifs).
- Custom emoji support: allow users to use custom emojis in posts. Fetch custom emojis from remote instances and display them properly.
- Pinned posts: allow users to 'feature' or 'pin' posts on their profile, and serve these featured posts via AP for other servers to see.
- Profile fields: allow users to set 'fields' on their profile: short key/value items that can display pronouns, links to websites, etc.
- **Move activity** -- use the ActivityPub `Move` activity to support migration of a user's profile across servers.
- **Sign-up flow** -- allow users to submit a sign-up request to an instance; allow admins to moderate sign-up requests.
### Q1 2023
### Early 2024
- **Big Feature** — Reports: allow users to file reports for abusive behavior etc. Expose the API for admins to view + act on reports. Handle federation of reports.
- List support: allow users to create lists of other users, which they can view as separate timelines.
- Polls support: allow users to create polls and vote in existing polls; federate the polls correctly via AP.
- **Non-replyable posts** -- design a non-replyable post path for GoToSocial based on https://github.com/mastodon/mastodon/issues/14762#issuecomment-1196889788; allow users to create non-replyable posts.
### Q2 2023
### And then...
- **Big Feature** — Sign-up flow. Allow users to submit a sign-up request to an instance. Allow admins to moderate sign-up requests.
- Direct conversations: Allow users to see all direct-message conversations they're a part of.
- Muting conversations: Allow users to mute notifications for conversations they're no longer interested in.
BETA TIME baby!
### Q3 2023
- **Big Feature** — Support the `Move` Activity, to allow users to move across instances and Fediverse implementations.
- More to be confirmed.
## Detailed To-do List
### API Groups + Endpoints
Unfilled check - not yet implemented.
Filled check - implemented.
Crossed out - will not be implemented / will be stubbed only.
- [ ] Client-To-Server (Client REST API)
- [ ] Token and sign-in
- [x] /api/v1/apps POST (Create an application)
- [ ] /api/v1/apps/verify_credentials GET (Verify an application works)
- [x] /oauth/authorize GET (Show authorize page to user)
- [x] /oauth/authorize POST (Get an OAuth access code for an app/user)
- [x] /oauth/token POST (Obtain a user-level access token)
- [ ] /oauth/revoke POST (Revoke a user-level access token)
- [x] /auth/sign_in GET (Show form for user sign-in)
- [x] /auth/sign_in POST (Validate username and password and sign user in)
- [ ] Accounts
- [x] /api/v1/accounts POST (Register a new account)
- [x] /api/v1/accounts/verify_credentials GET (Verify account credentials with a user token)
- [x] /api/v1/accounts/update_credentials PATCH (Update user's display name/preferences)
- [x] /api/v1/accounts/:id GET (Get account information)
- [x] /api/v1/accounts/:id/statuses GET (Get an account's statuses)
- [x] /api/v1/accounts/:id/followers GET (Get an account's followers)
- [x] /api/v1/accounts/:id/following GET (Get an account's following)
- [ ] /api/v1/accounts/:id/featured_tags GET (Get an account's featured tags)
- [ ] /api/v1/accounts/:id/lists GET (Get lists containing this account)
- [x] /api/v1/accounts/:id/follow POST (Follow this account)
- [x] /api/v1/accounts/:id/unfollow POST (Unfollow this account)
- [x] /api/v1/accounts/:id/block POST (Block this account)
- [x] /api/v1/accounts/:id/unblock POST (Unblock this account)
- [ ] /api/v1/accounts/:id/mute POST (Mute this account)
- [ ] /api/v1/accounts/:id/unmute POST (Unmute this account)
- [ ] /api/v1/accounts/:id/pin POST (Feature this account on profile)
- [ ] /api/v1/accounts/:id/unpin POST (Remove this account from profile)
- [ ] /api/v1/accounts/:id/note POST (Make a personal note about this account)
- [x] /api/v1/accounts/relationships GET (Check relationships with accounts)
- [ ] /api/v1/accounts/search GET (Search for an account)
- ~~/api/v1/accounts/:id/identity_proofs GET (Get identity proofs for this account)~~
- [x] Favourites
- [x] /api/v1/favourites GET (See faved statuses)
- [ ] Mutes
- [ ] /api/v1/mutes GET (See list of muted accounts)
- [x] Blocks
- [x] /api/v1/blocks GET (See list of blocked accounts)
- [x] Domain Blocks
- [x] /api/v1/domain_blocks GET (See list of domain blocks)
- [x] /api/v1/domain_blocks POST (Create a domain block)
- [x] /api/v1/domain_blocks DELETE (Remove a domain block)
- [ ] Filters
- [ ] /api/v1/filters GET (Get list of filters)
- [ ] /api/v1/filters/:id GET (View a filter)
- [ ] /api/v1/filters POST (Create a filter)
- [ ] /api/v1/filters/:id PUT (Update a filter)
- [ ] /api/v1/filters/:id DELETE (Remove a filter)
- [ ] Reports
- [ ] /api/v1/reports POST (File a report)
- [x] Follow Requests
- [x] /api/v1/follow_requests GET (View pending follow requests)
- [x] /api/v1/follow_requests/:id/authorize POST (Accept a follow request)
- [x] /api/v1/follow_requests/:id/reject POST (Reject a follow request)
- [ ] Featured Tags
- [ ] /api/v1/featured_tags GET (View featured tags)
- [ ] /api/v1/featured_tags POST (Feature a tag)
- [ ] /api/v1/featured_tags/:id DELETE (Unfeature a tag)
- ~~/api/v1/featured_tags/suggestions GET (See most used tags)~~
- [ ] Preferences
- [ ] /api/v1/preferences GET (Get user preferences)
- [ ] Statuses
- [x] /api/v1/statuses POST (Create a new status)
- [x] /api/v1/statuses/:id GET (View an existing status)
- [x] /api/v1/statuses/:id DELETE (Delete a status)
- [x] /api/v1/statuses/:id/context GET (View statuses above and below status ID)
- [x] /api/v1/statuses/:id/reblogged_by GET (See who has reblogged a status)
- [x] /api/v1/statuses/:id/favourited_by GET (See who has faved a status)
- [x] /api/v1/statuses/:id/favourite POST (Fave a status)
- [x] /api/v1/statuses/:id/unfavourite POST (Unfave a status)
- [x] /api/v1/statuses/:id/reblog POST (Reblog a status)
- [x] /api/v1/statuses/:id/unreblog POST (Undo a reblog)
- [ ] /api/v1/statuses/:id/mute POST (Mute notifications on a status)
- [ ] /api/v1/statuses/:id/unmute POST (Unmute notifications on a status)
- [ ] /api/v1/statuses/:id/pin POST (Pin a status to profile)
- [ ] /api/v1/statuses/:id/unpin POST (Unpin a status from profile)
- [x] /api/v1/statuses/:id/bookmark POST (Bookmark a status)
- [x] /api/v1/statuses/:id/unbookmark POST (Undo a bookmark)
- [x] Media
- [x] /api/v1/media POST (Upload a media attachment)
- [x] /api/v1/media/:id GET (Get a media attachment)
- [x] /api/v1/media/:id PUT (Update an attachment)
- [ ] Polls
- [ ] /api/v1/polls/:id GET (Show a poll)
- [ ] /api/v1/polls/:id/votes POST (Vote on a poll)
- [ ] Timelines
- [x] /api/v1/timelines/public GET (See the public/federated timeline)
- [ ] /api/v1/timelines/tag/:hashtag GET (Get public statuses that use hashtag)
- [x] /api/v1/timelines/home GET (View statuses from followed users)
- [ ] /api/v1/timelines/list/:list_id GET (Get statuses in given list)
- [ ] Conversations
- [ ] /api/v1/conversations GET (Get a list of direct message convos)
- [ ] /api/v1/conversations/:id DELETE (Delete a direct message convo)
- [ ] /api/v1/conversations/:id POST (Mark a conversation as read)
- [ ] Lists
- [ ] /api/v1/lists GET (Show a list of lists)
- [ ] /api/v1/lists/:id GET (Show a single list)
- [ ] /api/v1/lists POST (Create a new list)
- [ ] /api/v1/lists/:id PUT (Update a list)
- [ ] /api/v1/lists/:id DELETE (Delete a list)
- [ ] /api/v1/lists/:id/accounts GET (View which accounts are in a list)
- [ ] /api/v1/lists/:id/accounts POST (Add accounts to a list)
- [ ] /api/v1/lists/:id/accounts DELETE (Remove accounts from a list)
- [ ] Markers
- [ ] /api/v1/markers GET (Get saved timeline position)
- [ ] /api/v1/markers POST (Save timeline position)
- [x] Streaming
- [x] /api/v1/streaming WEBSOCKETS (Stream live events to user via websockets)
- [ ] Notifications
- [x] /api/v1/notifications GET (Get list of notifications)
- [x] /api/v1/notifications/:id GET (Get a single notification)
- [ ] /api/v1/notifications/clear POST (Clear all notifications)
- [ ] /api/v1/notifications/:id POST (Clear a single notification)
- [x] Search
- [x] /api/v2/search GET (Get search query results)
- [ ] Instance
- [x] /api/v1/instance GET (Get instance information)
- [x] /api/v1/instance PATCH (Update instance information)
- [x] /api/v1/instance/peers GET (Get list of federated servers)
- ~~ /api/v1/instance/activity GET (Instance activity over the last 3 months, binned weekly.)~~
- [x] Custom Emojis
- [x] /api/v1/custom_emojis GET (Show this server's custom emoji)
- [ ] Admin
- [x] /api/v1/admin/custom_emojis POST (Upload a custom emoji for instance-wide usage)
- [ ] /api/v1/admin/accounts GET (View accounts filtered by criteria)
- [ ] /api/v1/admin/accounts/:id GET (View admin level info about an account)
- [x] /api/v1/admin/accounts/:id/action POST (Perform an admin action on account)
- [ ] /api/v1/admin/accounts/:id/approve POST (Approve pending account)
- [ ] /api/v1/admin/accounts/:id/reject POST (Deny pending account)
- [ ] /api/v1/admin/accounts/:id/enable POST (Re-enable a disabled account)
- [ ] /api/v1/admin/accounts/:id/unsilence POST (Unsilence a silenced account)
- [ ] /api/v1/admin/accounts/:id/unsuspend POST (Unsuspend a suspended account)
- [ ] /api/v1/admin/reports GET (View all reports)
- [ ] /api/v1/admin/reports/:id GET (View a single report)
- [ ] /api/v1/admin/reports/:id/assign_to_self POST (Assign a report to the current admin account)
- [ ] /api/v1/admin/reports/:id/unassign POST (Unassign a report)
- [ ] /api/v1/admin/reports/:id/resolve POST (Mark a report as resolved)
- [ ] /api/v1/admin/reports/:id/reopen POST (Reopen a closed report)
- [ ] Announcements
- [ ] /api/v1/announcements GET (Show all current announcements)
- [ ] /api/v1/announcements/:id/dismiss POST (Mark an announcement as read)
- [ ] /api/v1/announcements/:id/reactions/:name PUT (Add a reaction to an announcement)
- [ ] /api/v1/announcements/:id/reactions/:name DELETE (Remove a reaction from an announcement)
- [ ] Oembed
- [ ] /api/oembed GET (Get oembed metadata for a status URL)
- ~~Proofs~~
- ~~/api/proofs GET (View identity proofs)~~
- ~~Trends~~
- ~~/api/v1/trends GET (Get a list of trending tags for the last week)~~
- ~~Directory~~
- ~~/api/v1/directory GET (Show profiles this server is aware of.)~~
- ~~Endorsements~~
- ~~/api/v1/endorsements GET (View existing endorsements)~~
- ~~Push~~
- ~~/api/v1/push/subscription POST (Subscribe to push notifications)~~
- ~~/api/v1/push/subscription GET (Get current subscription)~~
- ~~/api/v1/push/subscription PUT (Change notification types)~~
- ~~/api/v1/push/subscription DELETE (Delete current subscription)~~
- ~~Scheduled Statuses~~
- ~~/api/v1/scheduled_statuses GET (View scheduled statuses)~~
- ~~/api/v1/scheduled_statuses/:id GET (View a scheduled status)~~
- ~~/api/v1/scheduled_statuses/:id PUT (Schedule a status)~~
- ~~/api/v1/scheduled_statuses/:id DELETE (Cancel a scheduled status)~~
- ~~Suggestions~~
- ~~/api/v1/suggestions GET (Get suggested accounts to follow)~~
- ~~/api/v1/suggestions/:account_id DELETE (Delete a suggestion)~~
- [x] Bookmarks
- [x] /api/v1/bookmarks GET (See bookmarked statuses)
### Non-API tasks
- [ ] Server-To-Server (Federation protocol)
- [x] Mechanism to trigger side effects from client AP
- [x] Webfinger account lookups
- [ ] Federation modes
- [ ] 'Slow' federation
- [ ] Reputation scoring system for instances
- [x] 'Greedy' federation
- [ ] No federation (insulate this instance from the Fediverse)
- [ ] Allow list
- [x] Secure HTTP signatures (creation and validation)
- [x] Storage
- [x] Internal/statuses/preferences etc
- [x] Postgres interface
- [x] SQLite interface
- [x] Media storage
- [x] Local storage interface
- [x] S3 storage interface
- [x] Cache
- [x] In-memory cache
- [ ] Security features
- [x] Authorization middleware
- [ ] Rate limiting middleware
- [ ] Scope middleware
- [x] Permissions/ACL middleware for admins+moderators
- [ ] Documentation
- [x] Swagger API documentation
- [x] ReadTheDocs.io documentation
- [x] Deployment documentation
- [ ] App creation guide
- [ ] Tooling
- [ ] Database migration tool
- [x] Admin CLI tool
- [x] Build
- [x] Docker containerization
- [x] Dockerfile
- [x] docker-compose.yml
- [ ] Tests
- [ ] Unit/integration
- [x] 25% coverage
- [ ] 50% coverage
- [ ] 90%+ coverage
- [ ] Benchmarking

View file

@ -1,109 +0,0 @@
# Reports / Flag Activity Federation
This document contains design notes for GoToSocial's federated (s2s) Flag functionality.
## What do existing implementations do?
Information below is true as of Jan 2023. If you're reading this much later, the below things may no longer apply.
### Mastodon
Mastodon uses the Flag activity to federate reports to other AP servers.
The Activity is wrapped inside a Create, which is addressed To the Inbox of the offending account.
To preserve anonymity of the reporter, the instance Actor is used as the Actor of the Activity.
Examples of the unwrapped Flag:
```json
{
"@context": "https://www.w3.org/ns/activitystreams",
"actor": "https://example.org/actor",
"content": "misinfo: it's not a good morning",
"id": "https://example.org/341e866f-93f8-4755-9cf4-f8fb17f434fd",
"object": [
"https://bad.instance/users/tobi",
"https://bad.instance/users/tobi/statuses/01GP388K19DGXSV3SW2RXWM533"
],
"type": "Flag"
}
```
```json
{
"@context": "https://www.w3.org/ns/activitystreams",
"actor": "https://example.org/actor",
"content": "smellyyyyyyyyyyyyy",
"id": "https://example.org/3088184f-81b2-4545-8ce2-4cee4895449f",
"object": "https://bad.instance/users/tobi",
"type": "Flag"
}
```
The `content` field contains the report description.
The `object` field contains the reported Account URI and optionally one or more Note/Article/etc URIs. `object` value will be a string if just the Account URI is reported, or an array if the Account and one or more posts are being reported.
The `id` field is a generic URI that doesn't reveal any metadata. Trying to GET this URI gives a 404 Not Found error, which is OK since all the info needed to process the report is included in the Activity already.
### Misskey
Misskey uses the Flag activity to federate reports to other AP servers.
The Activity is wrapped inside a Create, which is addressed To the Inbox of the offending account.
To preserve anonymity of the reporter, the instance Actor is used as the Actor of the Activity.
Example of the unwrapped Flag:
```json
{
"@context": "https://www.w3.org/ns/activitystreams",
"actor": "https://example.org/users/909i45meeo",
"content": "Note: https://bad.instance/@tobi/statuses/01GPB56GPJ37JTK9HW308HQKBQ\n-----\nincites anti-police behaviour while being cute! ⛔",
"id": "https://example.org/db22128d-884e-4358-9935-6a7c3940535d",
"object": "https://bad.instance/users/tobi",
"type": "Flag"
}
```
The `content` field contains the report description. Unlike with Mastodon, `content` also seems to include one or more statuses, as opposed to including statuses in the `object` field.
The `object` field contains the reported Account URI.
Trying to dereference the `id` field for a Misskey report with `Accept: application/activity+json` gives a 200 OK, but the returned content is some HTML unrelated to the report, so functionally equivalent to Mastodon's 404 behavior. Again, this is not really a problem.
### Calckey
Same as Misskey. Example:
```json
{
"@context": "https://www.w3.org/ns/activitystreams",
"actor": "https://example.org/users/97wsu4gkns",
"content": "Note: https://bad.instance/@tobi/statuses/01GPB56GPJ37JTK9HW308HQKBQ\n-----\nTest report from Calckey",
"id": "https://example.org/b9a02404-d007-4b31-8dd6-bfc53387ad85",
"object": "https://bad.instance/users/tobi",
"type": "Flag"
}
```
### Pleroma / Akkoma
todo
### Friendica
Unsure: Friendica and GoToSocial still don't federate properly with one another (https://github.com/superseriousbusiness/gotosocial/issues/169) so it's hard to test this.
## What should GoToSocial do?
Since the above implementations of Flag seem fairly consistent, GoToSocial should do more or less the same thing when federating reports outwards. So GtS ought to adopt the Mastodon behavior:
- Wrap Flag Activity in a Create and deliver it to the offending account.
- Use the GtS instance Actor as the Actor of the Flag.
- Generate an ID that doesn't reveal who created the report.
- Include Actor and one or more Note / Article / etc URIs in the `object` field
For incoming reports, all the above fields should be handled in order to generate a report for admins to look at.

View file

@ -1,19 +1,20 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/*
GoToSocial
Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package action

View file

@ -1,333 +1,309 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/*
GoToSocial
Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package account
import (
"context"
"errors"
"fmt"
"os"
"text/tabwriter"
"time"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/state"
"github.com/superseriousbusiness/gotosocial/internal/validate"
"golang.org/x/crypto/bcrypt"
)
func initState(ctx context.Context) (*state.State, error) {
// Create creates a new account in the database using the provided flags.
var Create action.GTSAction = func(ctx context.Context) error {
var state state.State
state.Caches.Init()
state.Caches.Start()
state.Workers.Start()
// Set the state DB connection
dbConn, err := bundb.NewBunDBService(ctx, &state)
if err != nil {
return nil, fmt.Errorf("error creating dbConn: %w", err)
return fmt.Errorf("error creating dbservice: %s", err)
}
// Set the state DB connection
state.DB = dbConn
return &state, nil
}
func stopState(ctx context.Context, state *state.State) error {
if err := state.DB.Stop(ctx); err != nil {
return fmt.Errorf("error stopping dbConn: %w", err)
}
state.Workers.Stop()
state.Caches.Stop()
return nil
}
// Create creates a new account and user
// in the database using the provided flags.
var Create action.GTSAction = func(ctx context.Context) error {
state, err := initState(ctx)
if err != nil {
return err
}
username := config.GetAdminAccountUsername()
if username == "" {
return errors.New("no username set")
}
if err := validate.Username(username); err != nil {
return err
}
usernameAvailable, err := state.DB.IsUsernameAvailable(ctx, username)
usernameAvailable, err := dbConn.IsUsernameAvailable(ctx, username)
if err != nil {
return err
}
if !usernameAvailable {
return fmt.Errorf("username %s is already in use", username)
}
email := config.GetAdminAccountEmail()
if email == "" {
return errors.New("no email set")
}
if err := validate.Email(email); err != nil {
return err
}
emailAvailable, err := state.DB.IsEmailAvailable(ctx, email)
emailAvailable, err := dbConn.IsEmailAvailable(ctx, email)
if err != nil {
return err
}
if !emailAvailable {
return fmt.Errorf("email address %s is already in use", email)
}
password := config.GetAdminAccountPassword()
if err := validate.Password(password); err != nil {
if password == "" {
return errors.New("no password set")
}
if err := validate.NewPassword(password); err != nil {
return err
}
if _, err := state.DB.NewSignup(ctx, gtsmodel.NewSignup{
Username: username,
Email: email,
Password: password,
EmailVerified: true, // Assume cli user wants email marked as verified already.
PreApproved: true, // Assume cli user wants account marked as approved already.
}); err != nil {
return err
}
return stopState(ctx, state)
}
// List returns all existing local accounts.
var List action.GTSAction = func(ctx context.Context) error {
state, err := initState(ctx)
_, err = dbConn.NewSignup(ctx, username, "", false, email, password, nil, "", "", true, "", false)
if err != nil {
return err
}
users, err := state.DB.GetAllUsers(ctx)
if err != nil {
return err
}
fmtBool := func(b *bool) string {
if b == nil {
return "unknown"
}
if *b {
return "yes"
}
return "no"
}
fmtDate := func(t time.Time) string {
if t.Equal(time.Time{}) {
return "no"
}
return "yes"
}
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
fmt.Fprintln(w, "user\taccount\tapproved\tadmin\tmoderator\tsuspended\tconfirmed")
for _, u := range users {
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", u.Account.Username, u.AccountID, fmtBool(u.Approved), fmtBool(u.Admin), fmtBool(u.Moderator), fmtDate(u.Account.SuspendedAt), fmtDate(u.ConfirmedAt))
}
w.Flush()
return nil
return dbConn.Stop(ctx)
}
// Confirm sets a user to Approved, sets Email to the current
// UnconfirmedEmail value, and sets ConfirmedAt to now.
// Confirm sets a user to Approved, sets Email to the current UnconfirmedEmail value, and sets ConfirmedAt to now.
var Confirm action.GTSAction = func(ctx context.Context) error {
state, err := initState(ctx)
var state state.State
state.Caches.Init()
dbConn, err := bundb.NewBunDBService(ctx, &state)
if err != nil {
return err
return fmt.Errorf("error creating dbservice: %s", err)
}
// Set the state DB connection
state.DB = dbConn
username := config.GetAdminAccountUsername()
if username == "" {
return errors.New("no username set")
}
if err := validate.Username(username); err != nil {
return err
}
account, err := state.DB.GetAccountByUsernameDomain(ctx, username, "")
a, err := dbConn.GetAccountByUsernameDomain(ctx, username, "")
if err != nil {
return err
}
user, err := state.DB.GetUserByAccountID(ctx, account.ID)
u, err := dbConn.GetUserByAccountID(ctx, a.ID)
if err != nil {
return err
}
user.Approved = func() *bool { a := true; return &a }()
user.Email = user.UnconfirmedEmail
user.ConfirmedAt = time.Now()
if err := state.DB.UpdateUser(
ctx, user,
"approved", "email", "confirmed_at",
); err != nil {
updatingColumns := []string{"approved", "email", "confirmed_at"}
approved := true
u.Approved = &approved
u.Email = u.UnconfirmedEmail
u.ConfirmedAt = time.Now()
if err := dbConn.UpdateUser(ctx, u, updatingColumns...); err != nil {
return err
}
return stopState(ctx, state)
return dbConn.Stop(ctx)
}
// Promote sets admin + moderator flags on a user to true.
// Promote sets a user to admin.
var Promote action.GTSAction = func(ctx context.Context) error {
state, err := initState(ctx)
var state state.State
state.Caches.Init()
dbConn, err := bundb.NewBunDBService(ctx, &state)
if err != nil {
return err
return fmt.Errorf("error creating dbservice: %s", err)
}
// Set the state DB connection
state.DB = dbConn
username := config.GetAdminAccountUsername()
if username == "" {
return errors.New("no username set")
}
if err := validate.Username(username); err != nil {
return err
}
account, err := state.DB.GetAccountByUsernameDomain(ctx, username, "")
a, err := dbConn.GetAccountByUsernameDomain(ctx, username, "")
if err != nil {
return err
}
user, err := state.DB.GetUserByAccountID(ctx, account.ID)
u, err := dbConn.GetUserByAccountID(ctx, a.ID)
if err != nil {
return err
}
user.Admin = func() *bool { a := true; return &a }()
user.Moderator = func() *bool { a := true; return &a }()
if err := state.DB.UpdateUser(
ctx, user,
"admin", "moderator",
); err != nil {
admin := true
u.Admin = &admin
if err := dbConn.UpdateUser(ctx, u, "admin"); err != nil {
return err
}
return stopState(ctx, state)
return dbConn.Stop(ctx)
}
// Demote sets admin + moderator flags on a user to false.
// Demote sets admin on a user to false.
var Demote action.GTSAction = func(ctx context.Context) error {
state, err := initState(ctx)
var state state.State
state.Caches.Init()
dbConn, err := bundb.NewBunDBService(ctx, &state)
if err != nil {
return err
return fmt.Errorf("error creating dbservice: %s", err)
}
// Set the state DB connection
state.DB = dbConn
username := config.GetAdminAccountUsername()
if username == "" {
return errors.New("no username set")
}
if err := validate.Username(username); err != nil {
return err
}
a, err := state.DB.GetAccountByUsernameDomain(ctx, username, "")
a, err := dbConn.GetAccountByUsernameDomain(ctx, username, "")
if err != nil {
return err
}
user, err := state.DB.GetUserByAccountID(ctx, a.ID)
u, err := dbConn.GetUserByAccountID(ctx, a.ID)
if err != nil {
return err
}
user.Admin = func() *bool { a := false; return &a }()
user.Moderator = func() *bool { a := false; return &a }()
if err := state.DB.UpdateUser(
ctx, user,
"admin", "moderator",
); err != nil {
admin := false
u.Admin = &admin
if err := dbConn.UpdateUser(ctx, u, "admin"); err != nil {
return err
}
return stopState(ctx, state)
return dbConn.Stop(ctx)
}
// Disable sets Disabled to true on a user.
var Disable action.GTSAction = func(ctx context.Context) error {
state, err := initState(ctx)
var state state.State
state.Caches.Init()
dbConn, err := bundb.NewBunDBService(ctx, &state)
if err != nil {
return err
return fmt.Errorf("error creating dbservice: %s", err)
}
// Set the state DB connection
state.DB = dbConn
username := config.GetAdminAccountUsername()
if username == "" {
return errors.New("no username set")
}
if err := validate.Username(username); err != nil {
return err
}
account, err := state.DB.GetAccountByUsernameDomain(ctx, username, "")
a, err := dbConn.GetAccountByUsernameDomain(ctx, username, "")
if err != nil {
return err
}
user, err := state.DB.GetUserByAccountID(ctx, account.ID)
u, err := dbConn.GetUserByAccountID(ctx, a.ID)
if err != nil {
return err
}
user.Disabled = func() *bool { d := true; return &d }()
if err := state.DB.UpdateUser(
ctx, user,
"disabled",
); err != nil {
disabled := true
u.Disabled = &disabled
if err := dbConn.UpdateUser(ctx, u, "disabled"); err != nil {
return err
}
return stopState(ctx, state)
return dbConn.Stop(ctx)
}
// Password sets the password of target account.
var Password action.GTSAction = func(ctx context.Context) error {
state, err := initState(ctx)
var state state.State
state.Caches.Init()
dbConn, err := bundb.NewBunDBService(ctx, &state)
if err != nil {
return err
return fmt.Errorf("error creating dbservice: %s", err)
}
// Set the state DB connection
state.DB = dbConn
username := config.GetAdminAccountUsername()
if username == "" {
return errors.New("no username set")
}
if err := validate.Username(username); err != nil {
return err
}
password := config.GetAdminAccountPassword()
if err := validate.Password(password); err != nil {
if password == "" {
return errors.New("no password set")
}
if err := validate.NewPassword(password); err != nil {
return err
}
account, err := state.DB.GetAccountByUsernameDomain(ctx, username, "")
a, err := dbConn.GetAccountByUsernameDomain(ctx, username, "")
if err != nil {
return err
}
user, err := state.DB.GetUserByAccountID(ctx, account.ID)
u, err := dbConn.GetUserByAccountID(ctx, a.ID)
if err != nil {
return err
}
encryptedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
pw, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return fmt.Errorf("error hashing password: %s", err)
}
user.EncryptedPassword = string(encryptedPassword)
if err := state.DB.UpdateUser(
ctx, user,
"encrypted_password",
); err != nil {
u.EncryptedPassword = string(pw)
if err := dbConn.UpdateUser(ctx, u, "encrypted_password"); err != nil {
return err
}
return stopState(ctx, state)
return nil
}

View file

@ -1,165 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package media
import (
"bufio"
"context"
"fmt"
"os"
"path"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/state"
)
type list struct {
dbService db.DB
state *state.State
maxID string
limit int
out *bufio.Writer
}
func (l *list) GetAllMediaPaths(ctx context.Context, filter func(*gtsmodel.MediaAttachment) string) ([]string, error) {
res := make([]string, 0, 100)
for {
attachments, err := l.dbService.GetAttachments(ctx, l.maxID, l.limit)
if err != nil {
return nil, fmt.Errorf("failed to retrieve media metadata from database: %w", err)
}
for _, a := range attachments {
v := filter(a)
if v != "" {
res = append(res, v)
}
}
// If we got less results than our limit, we've reached the
// last page to retrieve and we can break the loop. If the
// last batch happens to contain exactly the same amount of
// items as the limit we'll end up doing one extra query.
if len(attachments) < l.limit {
break
}
// Grab the last ID from the batch and set it as the maxID
// that'll be used in the next iteration so we don't get items
// we've already seen.
l.maxID = attachments[len(attachments)-1].ID
}
return res, nil
}
func setupList(ctx context.Context) (*list, error) {
var state state.State
state.Caches.Init()
state.Caches.Start()
state.Workers.Start()
dbService, err := bundb.NewBunDBService(ctx, &state)
if err != nil {
return nil, fmt.Errorf("error creating dbservice: %w", err)
}
state.DB = dbService
return &list{
dbService: dbService,
state: &state,
limit: 200,
maxID: "",
out: bufio.NewWriter(os.Stdout),
}, nil
}
func (l *list) shutdown(ctx context.Context) error {
l.out.Flush()
err := l.dbService.Stop(ctx)
l.state.Workers.Stop()
l.state.Caches.Stop()
return err
}
var ListLocal action.GTSAction = func(ctx context.Context) error {
list, err := setupList(ctx)
if err != nil {
return err
}
defer func() {
// Ensure lister gets shutdown on exit.
if err := list.shutdown(ctx); err != nil {
log.Error(ctx, err)
}
}()
mediaPath := config.GetStorageLocalBasePath()
media, err := list.GetAllMediaPaths(
ctx,
func(m *gtsmodel.MediaAttachment) string {
if m.RemoteURL == "" {
return path.Join(mediaPath, m.File.Path)
}
return ""
})
if err != nil {
return err
}
for _, m := range media {
_, _ = list.out.WriteString(m + "\n")
}
return nil
}
var ListRemote action.GTSAction = func(ctx context.Context) error {
list, err := setupList(ctx)
if err != nil {
return err
}
defer func() {
// Ensure lister gets shutdown on exit.
if err := list.shutdown(ctx); err != nil {
log.Error(ctx, err)
}
}()
media, err := list.GetAllMediaPaths(
ctx,
func(m *gtsmodel.MediaAttachment) string {
return m.RemoteURL
})
if err != nil {
return err
}
for _, m := range media {
_, _ = list.out.WriteString(m + "\n")
}
return nil
}

View file

@ -1,61 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package prune
import (
"context"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
"github.com/superseriousbusiness/gotosocial/internal/log"
)
// All performs all media clean actions
var All action.GTSAction = func(ctx context.Context) error {
// Setup pruning utilities.
prune, err := setupPrune(ctx)
if err != nil {
return err
}
defer func() {
// Ensure pruner gets shutdown on exit.
if err := prune.shutdown(ctx); err != nil {
log.Error(ctx, err)
}
}()
if config.GetAdminMediaPruneDryRun() {
log.Info(ctx, "prune DRY RUN")
ctx = gtscontext.SetDryRun(ctx)
}
days := config.GetMediaRemoteCacheDays()
// Perform the actual pruning with logging.
prune.cleaner.Media().All(ctx, days)
prune.cleaner.Emoji().All(ctx, days)
// Perform a cleanup of storage (for removed local dirs).
if err := prune.storage.Storage.Clean(ctx); err != nil {
log.Error(ctx, "error cleaning storage: %v", err)
}
return nil
}

View file

@ -1,92 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package prune
import (
"context"
"fmt"
"github.com/superseriousbusiness/gotosocial/internal/cleaner"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/media"
"github.com/superseriousbusiness/gotosocial/internal/state"
gtsstorage "github.com/superseriousbusiness/gotosocial/internal/storage"
)
type prune struct {
dbService db.DB
storage *gtsstorage.Driver
manager *media.Manager
cleaner *cleaner.Cleaner
state *state.State
}
func setupPrune(ctx context.Context) (*prune, error) {
var state state.State
state.Caches.Init()
state.Caches.Start()
state.Workers.Start()
dbService, err := bundb.NewBunDBService(ctx, &state)
if err != nil {
return nil, fmt.Errorf("error creating dbservice: %w", err)
}
state.DB = dbService
//nolint:contextcheck
storage, err := gtsstorage.AutoConfig()
if err != nil {
return nil, fmt.Errorf("error creating storage backend: %w", err)
}
state.Storage = storage
//nolint:contextcheck
manager := media.NewManager(&state)
//nolint:contextcheck
cleaner := cleaner.New(&state)
return &prune{
dbService: dbService,
storage: storage,
manager: manager,
cleaner: cleaner,
state: &state,
}, nil
}
func (p *prune) shutdown(ctx context.Context) error {
errs := gtserror.NewMultiError(2)
if err := p.storage.Close(); err != nil {
errs.Appendf("error closing storage backend: %w", err)
}
if err := p.dbService.Stop(ctx); err != nil {
errs.Appendf("error stopping database: %w", err)
}
p.state.Workers.Stop()
p.state.Caches.Stop()
return errs.Combine()
}

View file

@ -1,57 +1,75 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/*
GoToSocial
Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package prune
import (
"context"
"fmt"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
"github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/media"
"github.com/superseriousbusiness/gotosocial/internal/state"
gtsstorage "github.com/superseriousbusiness/gotosocial/internal/storage"
)
// Orphaned prunes orphaned media from storage.
var Orphaned action.GTSAction = func(ctx context.Context) error {
// Setup pruning utilities.
prune, err := setupPrune(ctx)
var state state.State
state.Caches.Init()
dbService, err := bundb.NewBunDBService(ctx, &state)
if err != nil {
return err
return fmt.Errorf("error creating dbservice: %s", err)
}
defer func() {
// Ensure pruner gets shutdown on exit.
if err := prune.shutdown(ctx); err != nil {
log.Error(ctx, err)
}
}()
if config.GetAdminMediaPruneDryRun() {
log.Info(ctx, "prune DRY RUN")
ctx = gtscontext.SetDryRun(ctx)
storage, err := gtsstorage.AutoConfig()
if err != nil {
return fmt.Errorf("error creating storage backend: %w", err)
}
// Perform the actual pruning with logging.
prune.cleaner.Media().LogPruneOrphaned(ctx)
manager, err := media.NewManager(dbService, storage)
if err != nil {
return fmt.Errorf("error instantiating mediamanager: %s", err)
}
// Perform a cleanup of storage (for removed local dirs).
if err := prune.storage.Storage.Clean(ctx); err != nil {
log.Error(ctx, "error cleaning storage: %v", err)
dry := config.GetAdminMediaPruneDryRun()
pruned, err := manager.PruneOrphaned(ctx, dry)
if err != nil {
return fmt.Errorf("error pruning: %s", err)
}
if dry /* dick heyyoooooo */ {
log.Infof("DRY RUN: %d stored items are orphaned and eligible to be pruned", pruned)
} else {
log.Infof("%d stored items were orphaned and pruned", pruned)
}
if err := storage.Close(); err != nil {
return fmt.Errorf("error closing storage backend: %w", err)
}
if err := dbService.Stop(ctx); err != nil {
return fmt.Errorf("error closing dbservice: %s", err)
}
return nil

View file

@ -1,62 +0,0 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package prune
import (
"context"
"time"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
"github.com/superseriousbusiness/gotosocial/internal/log"
)
// Remote prunes old and/or unused remote media.
var Remote action.GTSAction = func(ctx context.Context) error {
// Setup pruning utilities.
prune, err := setupPrune(ctx)
if err != nil {
return err
}
defer func() {
// Ensure pruner gets shutdown on exit.
if err := prune.shutdown(ctx); err != nil {
log.Error(ctx, err)
}
}()
if config.GetAdminMediaPruneDryRun() {
log.Info(ctx, "prune DRY RUN")
ctx = gtscontext.SetDryRun(ctx)
}
t := time.Now().Add(-24 * time.Hour * time.Duration(config.GetMediaRemoteCacheDays()))
// Perform the actual pruning with logging.
prune.cleaner.Media().LogPruneUnused(ctx)
prune.cleaner.Media().LogUncacheRemote(ctx, t)
// Perform a cleanup of storage (for removed local dirs).
if err := prune.storage.Storage.Clean(ctx); err != nil {
log.Error(ctx, "error cleaning storage: %v", err)
}
return nil
}

View file

@ -1,19 +1,20 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/*
GoToSocial
Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package trans

View file

@ -1,19 +1,20 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/*
GoToSocial
Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package trans

View file

@ -1,26 +1,27 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/*
GoToSocial
Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package config
import (
"context"
"encoding/json"
"os"
"fmt"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
"github.com/superseriousbusiness/gotosocial/internal/config"
@ -38,8 +39,13 @@ var Config action.GTSAction = func(ctx context.Context) (err error) {
return err
}
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
err = enc.Encode(raw)
return err
// Marshal map to JSON
b, err := json.Marshal(raw)
if err != nil {
return err
}
// Print to stdout
fmt.Printf("%s\n", b)
return nil
}

View file

@ -1,19 +1,20 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/*
GoToSocial
Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package server
@ -25,21 +26,15 @@ import (
"os"
"os/signal"
"syscall"
"time"
"codeberg.org/gruf/go-sched"
"github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
"github.com/superseriousbusiness/gotosocial/internal/api"
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/middleware"
tlprocessor "github.com/superseriousbusiness/gotosocial/internal/processing/timeline"
"github.com/superseriousbusiness/gotosocial/internal/timeline"
"github.com/superseriousbusiness/gotosocial/internal/tracing"
"github.com/superseriousbusiness/gotosocial/internal/visibility"
"go.uber.org/automaxprocs/maxprocs"
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
"github.com/superseriousbusiness/gotosocial/internal/email"
@ -49,6 +44,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/httpclient"
"github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/media"
"github.com/superseriousbusiness/gotosocial/internal/messages"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
"github.com/superseriousbusiness/gotosocial/internal/oidc"
"github.com/superseriousbusiness/gotosocial/internal/processing"
@ -58,28 +54,14 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/transport"
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
"github.com/superseriousbusiness/gotosocial/internal/web"
// Inherit memory limit if set from cgroup
_ "github.com/KimMachineGun/automemlimit"
)
// Start creates and starts a gotosocial server
var Start action.GTSAction = func(ctx context.Context) error {
if _, err := maxprocs.Set(maxprocs.Logger(nil)); err != nil {
log.Infof(ctx, "could not set CPU limits from cgroup: %s", err)
}
var state state.State
// Initialize caches
state.Caches.Init()
state.Caches.Start()
defer state.Caches.Stop()
// Initialize Tracing
if err := tracing.Initialize(); err != nil {
return fmt.Errorf("error initializing tracing: %w", err)
}
// Open connection to the database
dbService, err := bundb.NewBunDBService(ctx, &state)
@ -98,88 +80,58 @@ var Start action.GTSAction = func(ctx context.Context) error {
return fmt.Errorf("error creating instance instance: %s", err)
}
// Create the client API and federator worker pools
// NOTE: these MUST NOT be used until they are passed to the
// processor and it is started. The reason being that the processor
// sets the Worker process functions and start the underlying pools
clientWorker := concurrency.NewWorkerPool[messages.FromClientAPI](-1, -1)
fedWorker := concurrency.NewWorkerPool[messages.FromFederator](-1, -1)
federatingDB := federatingdb.New(dbService, fedWorker)
// build converters and util
typeConverter := typeutils.NewConverter(dbService)
// Open the storage backend
storage, err := gtsstorage.AutoConfig()
if err != nil {
return fmt.Errorf("error creating storage backend: %w", err)
}
// Set the state storage driver
state.Storage = storage
// Build HTTP client (TODO: add configurables here)
client := httpclient.New(httpclient.Config{})
// Build HTTP client
client := httpclient.New(httpclient.Config{
AllowRanges: config.MustParseIPPrefixes(config.GetHTTPClientAllowIPs()),
BlockRanges: config.MustParseIPPrefixes(config.GetHTTPClientBlockIPs()),
Timeout: config.GetHTTPClientTimeout(),
TLSInsecureSkipVerify: config.GetHTTPClientTLSInsecureSkipVerify(),
})
// Initialize workers.
state.Workers.Start()
defer state.Workers.Stop()
// Add a task to the scheduler to sweep caches.
// Frequency = 1 * minute
// Threshold = 80% capacity
sweep := func(time.Time) { state.Caches.Sweep(80) }
job := sched.NewJob(sweep).Every(time.Minute)
_ = state.Workers.Scheduler.Schedule(job)
// Build handlers used in later initializations.
mediaManager := media.NewManager(&state)
// build backend handlers
mediaManager, err := media.NewManager(dbService, storage)
if err != nil {
return fmt.Errorf("error creating media manager: %s", err)
}
oauthServer := oauth.New(ctx, dbService)
typeConverter := typeutils.NewConverter(dbService)
filter := visibility.NewFilter(&state)
federatingDB := federatingdb.New(&state, typeConverter)
transportController := transport.NewController(&state, federatingDB, &federation.Clock{}, client)
federator := federation.NewFederator(&state, federatingDB, transportController, typeConverter, mediaManager)
transportController := transport.NewController(dbService, federatingDB, &federation.Clock{}, client)
federator := federation.NewFederator(dbService, federatingDB, transportController, typeConverter, mediaManager)
// Decide whether to create a noop email
// sender (won't send emails) or a real one.
// decide whether to create a noop email sender (won't send emails) or a real one
var emailSender email.Sender
if smtpHost := config.GetSMTPHost(); smtpHost != "" {
// Host is defined; create a proper sender.
// host is defined so create a proper sender
emailSender, err = email.NewSender()
if err != nil {
return fmt.Errorf("error creating email sender: %s", err)
}
} else {
// No host is defined; create a noop sender.
// no host is defined so create a noop sender
emailSender, err = email.NewNoopSender(nil)
if err != nil {
return fmt.Errorf("error creating noop email sender: %s", err)
}
}
// Initialize timelines.
state.Timelines.Home = timeline.NewManager(
tlprocessor.HomeTimelineGrab(&state),
tlprocessor.HomeTimelineFilter(&state, filter),
tlprocessor.HomeTimelineStatusPrepare(&state, typeConverter),
tlprocessor.SkipInsert(),
)
if err := state.Timelines.Home.Start(); err != nil {
return fmt.Errorf("error starting home timeline: %s", err)
// create the message processor using the other services we've created so far
processor := processing.NewProcessor(typeConverter, federator, oauthServer, mediaManager, storage, dbService, emailSender, clientWorker, fedWorker)
if err := processor.Start(); err != nil {
return fmt.Errorf("error creating processor: %s", err)
}
state.Timelines.List = timeline.NewManager(
tlprocessor.ListTimelineGrab(&state),
tlprocessor.ListTimelineFilter(&state, filter),
tlprocessor.ListTimelineStatusPrepare(&state, typeConverter),
tlprocessor.SkipInsert(),
)
if err := state.Timelines.List.Start(); err != nil {
return fmt.Errorf("error starting list timeline: %s", err)
}
// Create the processor using all the other services we've created so far.
processor := processing.NewProcessor(typeConverter, federator, oauthServer, mediaManager, &state, emailSender)
// Set state client / federator worker enqueue functions
state.Workers.EnqueueClientAPI = processor.EnqueueClientAPI
state.Workers.EnqueueFederator = processor.EnqueueFederator
/*
HTTP router initialization
*/
@ -189,27 +141,17 @@ var Start action.GTSAction = func(ctx context.Context) error {
return fmt.Errorf("error creating router: %s", err)
}
middlewares := []gin.HandlerFunc{
middleware.AddRequestID(config.GetRequestIDHeader()), // requestID middleware must run before tracing
}
if config.GetTracingEnabled() {
middlewares = append(middlewares, tracing.InstrumentGin())
}
middlewares = append(middlewares, []gin.HandlerFunc{
// note: hooks adding ctx fields must be ABOVE
// the logger, otherwise won't be accessible.
middleware.Logger(config.GetLogClientIP()),
// attach global middlewares which are used for every request
router.AttachGlobalMiddleware(
middleware.Logger(),
middleware.UserAgent(),
middleware.CORS(),
middleware.ExtraHeaders(),
}...)
// attach global middlewares which are used for every request
router.AttachGlobalMiddleware(middlewares...)
)
// attach global no route / 404 handler to the router
router.AttachNoRouteHandler(func(c *gin.Context) {
apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(errors.New(http.StatusText(http.StatusNotFound))), processor.InstanceGetV1)
apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(errors.New(http.StatusText(http.StatusNotFound))), processor.InstanceGet)
})
// build router modules
@ -238,7 +180,7 @@ var Start action.GTSAction = func(ctx context.Context) error {
wellKnownModule = api.NewWellKnown(processor) // .well-known endpoints
nodeInfoModule = api.NewNodeInfo(processor) // nodeinfo endpoint
activityPubModule = api.NewActivityPub(dbService, processor) // ActivityPub endpoints
webModule = web.New(dbService, processor) // web pages + user profiles + settings panels etc
webModule = web.New(processor) // web pages + user profiles + settings panels etc
)
// create required middleware
@ -250,11 +192,9 @@ var Start action.GTSAction = func(ctx context.Context) error {
// throttling
cpuMultiplier := config.GetAdvancedThrottlingMultiplier()
retryAfter := config.GetAdvancedThrottlingRetryAfter()
clThrottle := middleware.Throttle(cpuMultiplier, retryAfter) // client api
s2sThrottle := middleware.Throttle(cpuMultiplier, retryAfter) // server-to-server (AP)
fsThrottle := middleware.Throttle(cpuMultiplier, retryAfter) // fileserver / web templates
pkThrottle := middleware.Throttle(cpuMultiplier, retryAfter) // throttle public key endpoint separately
clThrottle := middleware.Throttle(cpuMultiplier) // client api
s2sThrottle := middleware.Throttle(cpuMultiplier) // server-to-server (AP)
fsThrottle := middleware.Throttle(cpuMultiplier) // fileserver / web templates
gzip := middleware.Gzip() // applied to all except fileserver
@ -266,7 +206,6 @@ var Start action.GTSAction = func(ctx context.Context) error {
wellKnownModule.Route(router, gzip, s2sLimit, s2sThrottle)
nodeInfoModule.Route(router, s2sLimit, s2sThrottle, gzip)
activityPubModule.Route(router, s2sLimit, s2sThrottle, gzip)
activityPubModule.RoutePublicKey(router, s2sLimit, pkThrottle, gzip)
webModule.Route(router, fsLimit, fsThrottle, gzip)
gts, err := gotosocial.NewServer(dbService, router, federator, mediaManager)
@ -278,17 +217,22 @@ var Start action.GTSAction = func(ctx context.Context) error {
return fmt.Errorf("error starting gotosocial service: %s", err)
}
// perform initial media prune in case value of MediaRemoteCacheDays changed
if err := processor.AdminMediaPrune(ctx, config.GetMediaRemoteCacheDays()); err != nil {
return fmt.Errorf("error during initial media prune: %s", err)
}
// catch shutdown signals from the operating system
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
sig := <-sigs // block until signal received
log.Infof(ctx, "received signal %s, shutting down", sig)
log.Infof("received signal %s, shutting down", sig)
// close down all running services in order
if err := gts.Stop(ctx); err != nil {
return fmt.Errorf("error closing gotosocial service: %s", err)
}
log.Info(ctx, "done! exiting...")
log.Info("done! exiting...")
return nil
}

View file

@ -1,19 +1,20 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/*
GoToSocial
Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package testrig
@ -32,116 +33,73 @@ import (
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
"github.com/superseriousbusiness/gotosocial/internal/api"
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/gotosocial"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/messages"
"github.com/superseriousbusiness/gotosocial/internal/middleware"
"github.com/superseriousbusiness/gotosocial/internal/oidc"
tlprocessor "github.com/superseriousbusiness/gotosocial/internal/processing/timeline"
"github.com/superseriousbusiness/gotosocial/internal/state"
"github.com/superseriousbusiness/gotosocial/internal/storage"
"github.com/superseriousbusiness/gotosocial/internal/timeline"
"github.com/superseriousbusiness/gotosocial/internal/tracing"
"github.com/superseriousbusiness/gotosocial/internal/visibility"
"github.com/superseriousbusiness/gotosocial/internal/web"
"github.com/superseriousbusiness/gotosocial/testrig"
)
// Start creates and starts a gotosocial testrig server
var Start action.GTSAction = func(ctx context.Context) error {
var state state.State
testrig.InitTestConfig()
testrig.InitTestLog()
if err := tracing.Initialize(); err != nil {
return fmt.Errorf("error initializing tracing: %w", err)
}
// Initialize caches and database
state.DB = testrig.NewTestDB(&state)
// New test db inits caches so we don't need to do
// that twice, we can just start the initialized caches.
state.Caches.Start()
defer state.Caches.Stop()
testrig.StandardDBSetup(state.DB, nil)
dbService := testrig.NewTestDB()
testrig.StandardDBSetup(dbService, nil)
var storageBackend *storage.Driver
if os.Getenv("GTS_STORAGE_BACKEND") == "s3" {
state.Storage, _ = storage.NewS3Storage()
storageBackend, _ = storage.NewS3Storage()
} else {
state.Storage = testrig.NewInMemoryStorage()
storageBackend = testrig.NewInMemoryStorage()
}
testrig.StandardStorageSetup(state.Storage, "./testrig/media")
testrig.StandardStorageSetup(storageBackend, "./testrig/media")
// Initialize workers.
state.Workers.Start()
defer state.Workers.Stop()
// Create client API and federator worker pools
clientWorker := concurrency.NewWorkerPool[messages.FromClientAPI](-1, -1)
fedWorker := concurrency.NewWorkerPool[messages.FromFederator](-1, -1)
// build backend handlers
transportController := testrig.NewTestTransportController(&state, testrig.NewMockHTTPClient(func(req *http.Request) (*http.Response, error) {
transportController := testrig.NewTestTransportController(testrig.NewMockHTTPClient(func(req *http.Request) (*http.Response, error) {
r := io.NopCloser(bytes.NewReader([]byte{}))
return &http.Response{
StatusCode: 200,
Body: r,
}, nil
}, ""))
mediaManager := testrig.NewTestMediaManager(&state)
federator := testrig.NewTestFederator(&state, transportController, mediaManager)
}, ""), dbService, fedWorker)
mediaManager := testrig.NewTestMediaManager(dbService, storageBackend)
federator := testrig.NewTestFederator(dbService, transportController, storageBackend, mediaManager, fedWorker)
emailSender := testrig.NewEmailSender("./web/template/", nil)
typeConverter := testrig.NewTestTypeConverter(state.DB)
filter := visibility.NewFilter(&state)
// Initialize timelines.
state.Timelines.Home = timeline.NewManager(
tlprocessor.HomeTimelineGrab(&state),
tlprocessor.HomeTimelineFilter(&state, filter),
tlprocessor.HomeTimelineStatusPrepare(&state, typeConverter),
tlprocessor.SkipInsert(),
)
if err := state.Timelines.Home.Start(); err != nil {
return fmt.Errorf("error starting home timeline: %s", err)
processor := testrig.NewTestProcessor(dbService, storageBackend, federator, emailSender, mediaManager, clientWorker, fedWorker)
if err := processor.Start(); err != nil {
return fmt.Errorf("error starting processor: %s", err)
}
state.Timelines.List = timeline.NewManager(
tlprocessor.ListTimelineGrab(&state),
tlprocessor.ListTimelineFilter(&state, filter),
tlprocessor.ListTimelineStatusPrepare(&state, typeConverter),
tlprocessor.SkipInsert(),
)
if err := state.Timelines.List.Start(); err != nil {
return fmt.Errorf("error starting list timeline: %s", err)
}
processor := testrig.NewTestProcessor(&state, federator, emailSender, mediaManager)
/*
HTTP router initialization
*/
router := testrig.NewTestRouter(state.DB)
middlewares := []gin.HandlerFunc{
middleware.AddRequestID(config.GetRequestIDHeader()), // requestID middleware must run before tracing
}
if config.GetTracingEnabled() {
middlewares = append(middlewares, tracing.InstrumentGin())
}
middlewares = append(middlewares, []gin.HandlerFunc{
middleware.Logger(config.GetLogClientIP()),
router := testrig.NewTestRouter(dbService)
// attach global middlewares which are used for every request
router.AttachGlobalMiddleware(
middleware.Logger(),
middleware.UserAgent(),
middleware.CORS(),
middleware.ExtraHeaders(),
}...)
// attach global middlewares which are used for every request
router.AttachGlobalMiddleware(middlewares...)
)
// attach global no route / 404 handler to the router
router.AttachNoRouteHandler(func(c *gin.Context) {
apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(errors.New(http.StatusText(http.StatusNotFound))), processor.InstanceGetV1)
apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(errors.New(http.StatusText(http.StatusNotFound))), processor.InstanceGet)
})
// build router modules
@ -154,7 +112,7 @@ var Start action.GTSAction = func(ctx context.Context) error {
}
}
routerSession, err := state.DB.GetSession(ctx)
routerSession, err := dbService.GetSession(ctx)
if err != nil {
return fmt.Errorf("error retrieving router session for session middleware: %w", err)
}
@ -165,13 +123,13 @@ var Start action.GTSAction = func(ctx context.Context) error {
}
var (
authModule = api.NewAuth(state.DB, processor, idp, routerSession, sessionName) // auth/oauth paths
clientModule = api.NewClient(state.DB, processor) // api client endpoints
fileserverModule = api.NewFileserver(processor) // fileserver endpoints
wellKnownModule = api.NewWellKnown(processor) // .well-known endpoints
nodeInfoModule = api.NewNodeInfo(processor) // nodeinfo endpoint
activityPubModule = api.NewActivityPub(state.DB, processor) // ActivityPub endpoints
webModule = web.New(state.DB, processor) // web pages + user profiles + settings panels etc
authModule = api.NewAuth(dbService, processor, idp, routerSession, sessionName) // auth/oauth paths
clientModule = api.NewClient(dbService, processor) // api client endpoints
fileserverModule = api.NewFileserver(processor) // fileserver endpoints
wellKnownModule = api.NewWellKnown(processor) // .well-known endpoints
nodeInfoModule = api.NewNodeInfo(processor) // nodeinfo endpoint
activityPubModule = api.NewActivityPub(dbService, processor) // ActivityPub endpoints
webModule = web.New(processor) // web pages + user profiles + settings panels etc
)
// these should be routed in order
@ -181,10 +139,9 @@ var Start action.GTSAction = func(ctx context.Context) error {
wellKnownModule.Route(router)
nodeInfoModule.Route(router)
activityPubModule.Route(router)
activityPubModule.RoutePublicKey(router)
webModule.Route(router)
gts, err := gotosocial.NewServer(state.DB, router, federator, mediaManager)
gts, err := gotosocial.NewServer(dbService, router, federator, mediaManager)
if err != nil {
return fmt.Errorf("error creating gotosocial service: %s", err)
}
@ -197,16 +154,16 @@ var Start action.GTSAction = func(ctx context.Context) error {
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, os.Interrupt, syscall.SIGTERM)
sig := <-sigs
log.Infof(ctx, "received signal %s, shutting down", sig)
log.Infof("received signal %s, shutting down", sig)
testrig.StandardDBTeardown(state.DB)
testrig.StandardStorageTeardown(state.Storage)
testrig.StandardDBTeardown(dbService)
testrig.StandardStorageTeardown(storageBackend)
// close down all running services in order
if err := gts.Stop(ctx); err != nil {
return fmt.Errorf("error closing gotosocial service: %s", err)
}
log.Info(ctx, "done! exiting...")
log.Info("done! exiting...")
return nil
}

View file

@ -1,26 +1,26 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/*
GoToSocial
Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"github.com/spf13/cobra"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action/admin/account"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action/admin/media"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action/admin/media/prune"
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action/admin/trans"
"github.com/superseriousbusiness/gotosocial/internal/config"
@ -55,18 +55,6 @@ func adminCommands() *cobra.Command {
config.AddAdminAccountCreate(adminAccountCreateCmd)
adminAccountCmd.AddCommand(adminAccountCreateCmd)
adminAccountListCmd := &cobra.Command{
Use: "list",
Short: "list all existing local accounts",
PreRunE: func(cmd *cobra.Command, args []string) error {
return preRun(preRunArgs{cmd: cmd})
},
RunE: func(cmd *cobra.Command, args []string) error {
return run(cmd.Context(), account.List)
},
}
adminAccountCmd.AddCommand(adminAccountListCmd)
adminAccountConfirmCmd := &cobra.Command{
Use: "confirm",
Short: "confirm an existing local account manually, thereby skipping email confirmation",
@ -171,44 +159,17 @@ func adminCommands() *cobra.Command {
adminMediaCmd := &cobra.Command{
Use: "media",
Short: "admin commands related to stored media / emojis",
Short: "admin commands related stored media attachments/emojis",
}
/*
ADMIN MEDIA LIST COMMANDS
*/
adminMediaListLocalCmd := &cobra.Command{
Use: "list-local",
Short: "admin command to list media on local storage",
PreRunE: func(cmd *cobra.Command, args []string) error {
return preRun(preRunArgs{cmd: cmd})
},
RunE: func(cmd *cobra.Command, args []string) error {
return run(cmd.Context(), media.ListLocal)
},
}
adminMediaListRemoteCmd := &cobra.Command{
Use: "list-remote",
Short: "admin command to list remote media cached on this instance",
PreRunE: func(cmd *cobra.Command, args []string) error {
return preRun(preRunArgs{cmd: cmd})
},
RunE: func(cmd *cobra.Command, args []string) error {
return run(cmd.Context(), media.ListRemote)
},
}
adminMediaCmd.AddCommand(adminMediaListLocalCmd, adminMediaListRemoteCmd)
/*
ADMIN MEDIA PRUNE COMMANDS
*/
adminMediaPruneCmd := &cobra.Command{
Use: "prune",
Short: "admin commands for pruning media from storage",
Short: "admin commands for pruning unused/orphaned media from storage",
}
config.AddAdminMediaPrune(adminMediaPruneCmd)
adminMediaPruneOrphanedCmd := &cobra.Command{
Use: "orphaned",
@ -223,32 +184,6 @@ func adminCommands() *cobra.Command {
config.AddAdminMediaPrune(adminMediaPruneOrphanedCmd)
adminMediaPruneCmd.AddCommand(adminMediaPruneOrphanedCmd)
adminMediaPruneRemoteCmd := &cobra.Command{
Use: "remote",
Short: "prune unused / stale media from storage, older than given number of days",
PreRunE: func(cmd *cobra.Command, args []string) error {
return preRun(preRunArgs{cmd: cmd})
},
RunE: func(cmd *cobra.Command, args []string) error {
return run(cmd.Context(), prune.Remote)
},
}
config.AddAdminMediaPrune(adminMediaPruneRemoteCmd)
adminMediaPruneCmd.AddCommand(adminMediaPruneRemoteCmd)
adminMediaPruneAllCmd := &cobra.Command{
Use: "all",
Short: "perform all media and emoji prune / cleaning commands",
PreRunE: func(cmd *cobra.Command, args []string) error {
return preRun(preRunArgs{cmd: cmd})
},
RunE: func(cmd *cobra.Command, args []string) error {
return run(cmd.Context(), prune.All)
},
}
config.AddAdminMediaPrune(adminMediaPruneAllCmd)
adminMediaPruneCmd.AddCommand(adminMediaPruneAllCmd)
adminMediaCmd.AddCommand(adminMediaPruneCmd)
adminCmd.AddCommand(adminMediaCmd)

View file

@ -1,19 +1,20 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/*
GoToSocial
Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main

View file

@ -1,19 +1,20 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/*
GoToSocial
Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main

View file

@ -1,29 +1,28 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/*
GoToSocial
Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"log"
"os"
godebug "runtime/debug"
"runtime/debug"
"strings"
"codeberg.org/gruf/go-debug"
"github.com/spf13/cobra"
_ "github.com/superseriousbusiness/gotosocial/docs"
@ -61,14 +60,9 @@ func main() {
// add subcommands
rootCmd.AddCommand(serverCommands())
rootCmd.AddCommand(testrigCommands())
rootCmd.AddCommand(debugCommands())
rootCmd.AddCommand(adminCommands())
if debug.DEBUG {
// only add testrig if debug enabled.
rootCmd.AddCommand(testrigCommands())
} else if len(os.Args) > 1 && os.Args[1] == "testrig" {
log.Fatalln("gotosocial must be built and run with the DEBUG enviroment variable set to enable and access testrig")
}
// run
if err := rootCmd.Execute(); err != nil {
@ -79,7 +73,7 @@ func main() {
// version will build a version string from binary's stored build information.
func version() string {
// Read build information from binary
build, ok := godebug.ReadBuildInfo()
build, ok := debug.ReadBuildInfo()
if !ok {
return ""
}

View file

@ -1,19 +1,20 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/*
GoToSocial
Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main

View file

@ -1,19 +1,20 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/*
GoToSocial
Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main

26
docs/admin/admin_panel.md Normal file
View file

@ -0,0 +1,26 @@
# Admin Control Panel
The GoToSocial admin panel is a simple webclient that uses the [admin api routes](https://docs.gotosocial.org/en/latest/api/swagger/#operations-tag-admin) to manage your instance. It uses the same OAUTH mechanism as normal clients (with scope: admin), and as such can be hosted anywhere, separately from your instance, or run locally. A public installation is available here: [https://gts.superseriousbusiness.org/admin](https://gts.superseriousbusiness.org/admin).
## Using the panel
To use the Admin API your account has to be promoted as such:
```
./gotosocial --config-path ./config.yaml admin account promote --username YOUR_USERNAME
```
After this, you can enter your instance domain in the login field (auto-filled if you run GoToSocial on the same domain), and login like you would with
any other client.
<p align="middle">
<img src="../../assets/admin-panel.png">Screenshot of the GoToSocial admin panel, showing the fields to change an instance's settings</img>
</p>
You can change the instance's settings like the title and descriptions, and add/remove/change domain blocks including a bulk import/export.
## Building the panel
Build requirements: some version of [Node.js](https://nodejs.org) and yarn.
```
yarn install --cwd web/source
BUDO_BUILD=1 node web/source
```
See also: [Contributing.md Stylesheet / Web dev](https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md#stylesheet--web-dev)

View file

@ -1,37 +1,18 @@
# Backup and Restore
As the GoToSocial database contains the instance as well as all user signing keys it is vital to back it up. If you lose these keys you'll never be able to federate from this domain again. Don't forget to also encrypt your backups in order to keep the data safe at rest.
In certain conditions, it may be desirable to be able to back up a GoToSocial instance, and then to restore it again later, or just save the backup somewhere.
Aside from disaster recovery, there are other good reasons to keep backups. Some potential scenarios for you to consider:
Some potential scenarios:
* You want to close down your instance but you might create it again later and you don't want to break federation.
* You need to migrate to a different database for some reason (Postgres => SQLite or vice versa).
* You want to keep regular backups of your data just in case something happens.
* You want to migrate from GoToSocial to a different Fediverse server, or from a different Fediverse server to GoToSocial.
* You're about to hack around on your instance and you want to make a quick backup so you don't lose everything if you mess up.
## What to backup
There are a few different ways of doing this, most of which require some technical knowledge.
### Database
Most backup tools have built-in support for common databases like PostgreSQL and SQLite. Ensure you review their documentation first as they often spell out certain considerations and conditions that need to be met for backups to complete and restore successfully.
### Media
Local media should be backed up. You can use the [GoToSocial CLI](cli.md#gotosocial-admin-media-list-local) to list all media files that belong to your instance and its users.
Remote media does not have to be backed up. This can be a good way to keep the size of your backups down. Remote media will be fetched from the origin instance, much like how it'll be fetched again if it got pruned due to media retention.
## How to backup
You can go about this a few different ways:
* Imaging the VMs/machines your instance and database runs on
* Dumping GoToSocial's state with the CLI
* Backing up database and media files
* Backup software
Though setting up backup software can be a bit more work, it's by far the best option. It ensures consistent and encrypted backups and can protect you against filesystem corruption in a way that taking disk snapshots and copying the raw database and media files won't.
### Image your disk
## Image your disk
If you're running GoToSocial on a VPS (a remote machine in the cloud), arguably the easiest way to preserve all of your database entries and media is to image the disk attached to the VPS. This will preserve the whole disk. Many VPS providers offer the option of automatically creating backups on a timer, so you'll always be able to restore if your data is lost.
@ -47,7 +28,24 @@ Disadvantages:
* Will probably also preserve stuff you don't need, from other programs running on the same machine.
* Vendor lock-in, difficult to move the data around.
### Use the GoToSocial CLI
## Back up your database files
Regardless of whether you're using Postgres or SQLite as your GoToSocial database, it's possible to simply back up the database files directly by using something like [rclone](https://rclone.org/), or following best practices for [backing up Postgres data](https://www.postgresql.org/docs/9.1/backup.html) or [SQLite data](https://sqlite.org/backup.html).
Advantages:
* Backups are relatively portable - you can move data from one machine to another.
* Well-documented procedure with a lot of guides and tooling available.
* Lots of different ways of doing your backups, depending on what you need.
Disadvantages:
* Can be a bit fiddly to set up initially.
* You need to figure out where to keep your backups.
* Restoring from backups can be a pain.
* Unless you back up media as well, references to media attachments in your db will be broken.
## Use the GoToSocial CLI
The GoToSocial CLI tool also provides commands for backing up and restoring data from your instance, which will preserve the *bare-minimum* necessary data to backup and restore your instance, without breaking federation with other instances.
@ -90,7 +88,7 @@ The backup file produced will be in the form of a line-separated series of JSON
{"type":"instance","id":"01BZDDRPAB8J645ABY31HHF68Y","createdAt":"2021-09-08T10:00:54.763912Z","domain":"localhost:8080","title":"localhost:8080","uri":"http://localhost:8080","reputation":0}
```
For information on how to use the commands to import/export, see [here](cli.md#gotosocial-admin-export). Though the `export` command won't backup media, you can use the [`media list-local`](cli.md#gotosocial-admin-media-list-local) command to figure out which media files you should keep.
For information on how to use the commands to import/export, see [here](cli.md#gotosocial-admin-export).
Advantages:
@ -100,123 +98,5 @@ Advantages:
Disadvantages:
* Loss of statuses/faves/etc: don't do a backup/restore this way unless you're willing to drop stuff.
* Loss of statuses/media/etc: don't do a backup/restore this way unless you're willing to drop stuff.
* You need to use the GtS CLI tool to insert data back into a database, unless you write custom tooling for it.
### Back up your database files and media
Regardless of whether you're using PostgreSQL or SQLite as your GoToSocial database, it's possible to simply back up the database files directly by using something like [rclone](https://rclone.org/), or following best practices for [backing up Postgres data](https://www.postgresql.org/docs/15/backup.html) or [SQLite data](https://sqlite.org/backup.html).
Use the [GoToSocial CLI](cli.md#gotosocial-admin-media-list-local) to get a list of media files you need to safeguard.
Advantages:
* Backups are relatively portable - you can move data from one machine to another.
* Well-documented procedure with a lot of guides and tooling available.
* Lots of different ways of doing your backups, depending on what you need.
Disadvantages:
* Can be a bit fiddly to set up initially.
* You need to figure out where to keep your backups.
* Restoring from backups can be a pain.
* Unless you back up media as well, references to media attachments in your db will be broken.
### Backup software
Backup software is created with the specific purpose of helping you create, manage and restore your backups. It typically knows how to safely backup your database so you don't have to be an expert on how to do PostgreSQL or SQLite backups. It can backup from the filesystem too.
Though the same advantages and disadvantages roughly apply as with backing up the database files directly, this approach does have some nice extras:
* Backups are highly portable and can be used to restore the database from 0
* Backups happen on a regular schedule and with configurable retention policies
* Backups are incremental and compressed to save on storage and bandwidth
* Backups are encrypted
* Built-in tooling to list your snapshots and restore from them
!!! tip
[Rsync.net](https://rsync.net/), [BorgBase](https://www.borgbase.com/) and [Hetzner Storage](https://www.hetzner.com/storage/storage-box) provide affordable storage that you can use as a backup target. Rsync.net has a special Borg-only backup product that is much cheaper than their regular storage product. If you only want to use them for backups managed with Borg, [sign up here instead](https://www.rsync.net/products/borg.html).
#### Borgmatic
[Borgmatic](https://torsion.org/borgmatic/) is a utility to help perform backups using [Borg](https://www.borgbackup.org/). It's driven by a declarative configuration file using YAML. BorgBase, Rsync.net and Hetzner all support Borg.
!!! warning
When initialising the Borg repository, ensure you set it up with a strong encryption key and store that key somewhere safely. Without it you won't be able to decrypt your backups in the future. The ArchWiki entry on Borgmatic explains how to safely pass your encryption key to Borgmatic without storing it plain text in its configuration file.
How to backup databases with Borgmatic has its own [documentation page](https://torsion.org/borgmatic/docs/how-to/backup-your-databases/) that you should review. A simple `config.yaml` for Borgmatic with GoToSocial using SQLite looks like this:
```yaml
location:
repositories:
- path: ssh://<find it in your provider control panel>
label: <anything but typically the provider, for example borgbase>
patterns_from:
- /etc/borgmatic/gotosocial_patterns
storage:
compression: auto,zstd
archive_name_format: '{hostname}-{now:%Y-%m-%d-%H%M%S}'
retries: 5
retry_wait: 30
retention:
keep_daily: 7
keep_weekly: 6
keep_monthly: 12
hooks:
before_backup:
- /usr/bin/systemctl stop gotosocial
after_backup:
- /usr/bin/systemctl start gotosocial
sqlite_databases:
- name: gotosocial
path: /path/to/sqlite.db
```
For PostgreSQL, you'll want to use `postgresql_databases` instead.
The file mentioned in `patterns_from` can be created by transforming the output from the [GoToSocial CLI](cli.md#gotosocial-admin-media-list-local). In order to generate the right patterns you can use the [`media-to-borg-patterns.py`](https://github.com/superseriousbusiness/gotosocial/tree/main/example/borgmatic/media-to-borg-patterns.py) script. How Borg patterns work is explained in [their documentation](https://man.archlinux.org/man/borg-patterns.1).
You'll need to put that file on your GoToSocial instance and make sure the file is executable. It requires Python 3 which you will already have if you have Borg and Borgmatic installed. It only depends on the Python standard library.
!!! note
For this to work reliably, you should ensure that the [storage-local-base-path](../configuration/storage.md) in your GoToSocial configuration uses an absolute path. Otherwise you'll have to tweak the paths yourself.
```sh
$ gotosocial admin media list-local | \
/path/to/media-to-borg-patterns.py \
<storage-local-base-path>
```
This will output a pattern set looking roughly like this to your console:
```
R <storage-local-base-path>
+ pp:<storage-local-base-path>/<account ID>
- <storage-local-base-path>/*
```
!!! tip
You can view the help by passing `--help` to `media-to-borg-patterns.py`. It can write the output to a file directly by passing the location of a file as the last argument to the script.
Given this set of patterns, Borg will start looking for files starting from `<storage-local-base-path>`. Anything that matches the path prefix, `pp:` will be included. Everything else will match the last pattern, excluding it from the archive.
On a single-user instance, you can run this command once and inline the patterns directly in your Borgmatic configuration [using the `patterns` key](https://torsion.org/borgmatic/docs/reference/configuration/). On multi-user instances you should run this after a user signs up. Alternatively, you can run it every time before you do a backup.
If you're running Borgmatic as a systemd service, you can [create a drop-in](https://wiki.archlinux.org/title/systemd#Drop-in_files) for `borgmatic.service` and run the pattern generation before the backup is started with:
```ini
[Service]
ExecStartPre=/path/to/gotosocial admin media list-local | /path/to/media-to-borg-patterns.py <storage-local-base-path> /etc/borgmatic/gotosocial_patterns
```
Documentation that's good to review:
* Borgmatic [configuration reference](https://torsion.org/borgmatic/docs/reference/configuration/)
* ArchWiki entry [on Borgmatic](https://wiki.archlinux.org/title/Borgmatic)
* ArchWiki entry [on Borg](https://wiki.archlinux.org/title/Borg_backup)
* BorgBase [documentation](https://docs.borgbase.com/)
* Hetzner community guide on [setting up Borgmatic](https://community.hetzner.com/tutorials/install-and-configure-borgmatic)

View file

@ -179,7 +179,7 @@ Flags:
Example:
```bash
gotosocial admin account password --username some_username --password some_really_good_password --config-path config.yaml
gotosocial admin account password --username some_username --pasword some_really_good_password --config-path config.yaml
```
### gotosocial admin export
@ -255,18 +255,6 @@ Example:
gotosocial admin import --path example.json --config-path config.yaml
```
### gotosocial admin media list-local
This command can be used to list local media. Local media is media that belongs to posts by users with an account on the instance.
The output will be a list of files. The list can be used to drive your backups.
### gotosocial admin media list-remote
This is the corollary to list-local, but instead lists media from remote instances. Remote media belongs to other instances, but was attached to a post we received over federation and have potentially cached locally.
The output will be a list of URLs to retrieve the original content from. GoToSocial automatically retrieves remote media when it needs it, so you should never need to do so yourself.
### gotosocial admin media prune orphaned
This command can be used to prune orphaned media from your GoToSocial.
@ -299,40 +287,3 @@ Example (for real):
```bash
gotosocial admin media prune orphaned --dry-run=false
```
### gotosocial admin media prune remote
This command can be used to prune unused/stale remote media from your GoToSocial.
Stale media means avatars/headers/status attachments from remote instances that are older than `media-remote-cache-days`.
These items will be refetched later on demand, if necessary.
Unused media means avatars/headers/status attachments which are not currently in use by an account or status.
**This command only works when GoToSocial is not running, since it acquires an exclusive lock on storage. Stop GoToSocial first before running this command!**
```text
prune unused/stale remote media from storage, older than given number of days
Usage:
gotosocial admin media prune remote [flags]
Flags:
--dry-run perform a dry run and only log number of items eligible for pruning (default true)
-h, --help help for remote
```
By default, this command performs a dry run, which will log how many items can be pruned. To do it for real, add `--dry-run=false` to the command.
Example (dry run):
```bash
gotosocial admin media prune remote
```
Example (for real):
```bash
gotosocial admin media prune remote --dry-run=false
```

View file

@ -1,57 +0,0 @@
# Admin Settings
The GoToSocial Settings interface uses the [admin api routes](https://docs.gotosocial.org/en/latest/api/swagger/#operations-tag-admin) to manage your instance. It's combined with the [User settings](../user_guide/settings.md) and uses the same OAUTH mechanism as normal clients (with scope: admin).
## Account permissions
To use the Admin API your account has to be promoted as such:
```
./gotosocial --config-path ./config.yaml admin account promote --username YOUR_USERNAME
```
After this, you can enter your instance domain in the login field (auto-filled if you run GoToSocial on the same domain), and login like you would with any other client.
## Instance Settings
![Screenshot of the GoToSocial admin panel, showing the fields to change an instance's settings](../assets/admin-settings.png)
Here you can set various metadata for your instance, like the displayed name, thumbnail image, description texts (HTML), and contact username and email.
## Actions
You can use media cleanup to remove remote media older than the specified number of days. This also removes unused headers and avatars.
## Federation
![List of suspended instances, with a field to filter/add new blocks. Below is a link to the bulk import/export interface](../assets/admin-settings-federation.png)
In the federation section you can influence which instances you federate with, through adding domain blocks. You can enter a domain to suspend in the search field, which will filter the list to show you if you already have a block for it. Clicking 'suspend' gives you a form to add a public and/or private comment, and submit to add the block. Adding a suspension will suspend all the currently known accounts on the instance, and prevent any new interactions with any user on the blocked instance.
### Bulk import/export
Through the link at the bottom of the Federation section (or going to `/settings/admin/federation/import-export`) you can do bulk import/export of your domain blocklist.
![List of domains included in an import, providing ways to select some or all of them, change their domains, and update the use of subdomains.](../assets/admin-settings-federation-import-export.png)
Upon importing a list, either through the input field or from a file, you can review the entries in the list before importing a subset. You'll also be warned for entries that use subdomains, providing an easy way to change them to the main domain.
## Reports
![List of reports for testing, one resolved and one open.](../assets/admin-settings-reports.png)
The reports section shows a list of reports, originating from your local users, or remote instances (shown anonymously as just the name of the instance, without specific username).
Clicking a report shows if it was resolved (with the reasoning if available), more information, and a list of reported toots if selected by the reporting user.
## Custom Emoji
Custom Emoji will be automatically fetched when included in remote toots, but to use them in your own posts they have to be enabled on your instance.
### Local
![Local custom emoji section, showing an overview of custom emoji sorted by category. There are a lot of garfields.](../assets/admin-settings-emoji-local.png)
This section shows an overview of all the custom emoji enabled on your instance, sorted by their category. Clicking an emoji shows it's details, and provides options to change the category or image, or delete it completely. The shortcode cannot be updated here, you would have to upload it with the new shortcode yourself (and optionally delete the old one).
Below the overview you can upload your own custom emoji, after previewing how they look in a toot. PNG and (animated) GIF's are supported.
### Remote
![Remote custom emoji section, showing a list of 3 emoji parsed from the entered toot, garfield, blobfoxbox and blobhajmlem. They can be selected, their shortcode can be tweaked, and they can be assigned to a category, before submitting as a copy or delete operation](../assets/admin-settings-emoji-remote.png)
Through the 'remote' section, you can look up a link to any remote toots (provided the instance isn't suspended). If they use any custom emoji they will be listed, providing an easy way to copy them to the local emoji (for use in your own toots), or disable them ( hiding them from toots).
**Note:** as the testrig server does not federate, this feature can't be used in development (500: Internal Server Error).

View file

@ -1,86 +0,0 @@
# Caching API responses
It is possible to cache certain API responses to offload the GoToSocial process from having to handle all requests. We don't recommend caching responses to requests under `/api`.
When using a [split domain](../host-account-domain.md) deployment style, you need to ensure you configure caching on the host domain. The account domain should only be issuing redirects to the host domain which clients will automatically remember.
!!! warning "There are only two hard things in computer science"
Configuring caching incorrectly can result into all kinds of problems. Follow this guide carefully and thoroughly test your modifications. Don't cache endpoints that require authentication without taking the `Authorization` header into account.
## Endpoints
### Webfinger and hostmeta
Requests to `/.well-known/webfinger` and `/.well-known/host-meta` can be safely cached. Do be careful to ensure any caching strategy takes query parameters into account when caching webfinger requests as requests to that endpoint are of the form `?resource=acct:@username@domain.tld`.
### Public keys
Many implementations will regularly request the public key for a user in order to validate the signature on a message they received. This will happen whenever a message gets federated amongst other things. These keys are long lived, essentially eternal, and can thus be cached with a long lifetime.
## Configuration snippets
### nginx
For nginx, you'll need to start by configuring a cache zone. The cache zone must be created in the `http` section, not within `server` or `location`.
```nginx
http {
...
proxy_cache_path /var/cache/nginx keys_zone=gotosocial_ap_public_responses:10m inactive=1w;
}
```
This configures a cache of 10MB whose entries will be kept up to one week if they're not accessed.
The zone is named `gotosocial_ap_public_responses` but you can name it whatever you want. 10MB is a lot of cache keys; you can probably use a smaller value on small instances.
Second, we need to update our GoToSocial nginx configuration to actually use the cache for the endpoints we want to cache.
```nginx
server {
server_name social.example.org;
location ~ /.well-known/(webfinger|host-meta)$ {
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache gotosocial_ap_public_responses;
proxy_cache_background_update on;
proxy_cache_key $scheme://$host$uri$is_args$query_string;
proxy_cache_valid 200 10m;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504 http_429;
proxy_cache_lock on;
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://localhost:8080;
}
location ~ ^\/users\/(?:[a-z0-9_\.]+)\/main-key$ {
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache gotosocial_ap_public_responses;
proxy_cache_background_update on;
proxy_cache_key $scheme://$host$uri;
proxy_cache_valid 200 604800s;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504 http_429;
proxy_cache_lock on;
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://localhost:8080;
}
```
The `proxy_pass` and `proxy_set_header` are mostly the same, but the `proxy_cache*` entries warrant some explanation:
- `proxy_cache gotosocial_ap_public_responses` tells nginx to use the `gotosocial_ap_public_responses` cache zone we previously created. If you named it something else, you should change this value
- `proxy_cache_background_update on` means nginx will try and refresh a cached resource that's about to expire in the background, to ensure it has a current copy on disk
- `proxy_cache_key` is configured in such a way that it takes the query string into account for caching. So a request for `.well-known/webfinger?acct=user1@example.org` and `.well-known/webfinger?acct=user2@example.org` are not seen as the same.
- `proxy_cache_valid 200 10m;` means we only cache 200 responses from GTS and for 10 minutes. You can add additional lines of these, like `proxy_cache_valid 404 1m;` to cache 404 responses for 1 minute
- `proxy_cache_use_stale` tells nginx it's allowed to use a stale cache entry (so older than 10 minutes) in certain cases
- `proxy_cache_lock on` means that if a resource is not cached and there's multiple concurrent requests for them, the queries will be queued up so that only one request goes through and the rest is then answered from cache
- `add_header X-Cache-Status $upstream_cache_status` will add an `X-Cache-Status` header to the response so you can check if things are getting cached. You can remove this.
The provided configuration will serve a stale response in case there's an error proxying to GoToSocial, if our connection to GoToSocial times out, if GoToSocial returns a `5xx` status code or if GoToSocial returns 429 (Too Many Requests). The `updating` value says that we're allowed to serve a stale entry if nginx is currently in the process of refreshing its cache. Because we configured `inactive=1w` in the `proxy_cache_path` directive, nginx may serve a response up to one week old if the conditions in `proxy_cache_use_stale` are met.

View file

@ -1,143 +0,0 @@
# Caching assets and media
When you've configured your GoToSocial instance with local storage for media, you can use your [reverse proxy](../../getting_started/reverse_proxy/index.md) to serve these files directly and cache them. This avoids hitting GoToSocial for these requests and reverse proxies can typically serve assets faster than GoToSocial.
You can also use your reverse proxy to cache the GoToSocial web UI assets, like the CSS and images it uses.
When using a [split domain](../host-account-domain.md) deployment style, you need to ensure you configure caching of the assets and media on the host domain.
!!! warning "Media pruning"
If you've configured media pruning, you need to ensure that when media is not found on disk the request is still sent on to GoToSocial. This will ensure the media is fetched again from the remote instance and subsequent requests for this media will then be handled by your reverse proxy again.
## Endpoints
There are 2 endpoints that serve assets we can serve and cache:
* `/assets` which contains fonts, CSS, images etc. for the web UI
* `/fileserver` which serves attachments for status posts when using the local storage backend
The filesystem location of `/assets` is defined by the [`web-asset-base-dir`](../../configuration/web.md) configuration option. Files under `/fileserver` are retrieved from the [`storage-local-base-path`](../../configuration/storage.md).
## Configuration
### Apache 2.4
This is intended to behave identical to the nginx section below.
The `Cache-Control` header is manually set to merge the values
from the configuration and the `expires` directive to avoid
breakage from having two header lines. `Header set` defaults
to ` onsuccess`, so it is also not added to error responses.
Assuming your GtS installation is rooted in `/opt/GtS` with a
`storage` subdirectory, and the webserver has been given access,
add the following section to the vhost:
```
<Directory /opt/GtS/web/assets>
Options None
AllowOverride None
Require all granted
ExpiresActive on
ExpiresDefault A300
Header set Cache-Control "public, max-age=300"
</Directory>
RewriteRule "^/assets/(.*)$" "/opt/GtS/web/assets/$1" [L]
<Directory /opt/GtS/storage>
Options None
AllowOverride None
Require all granted
ExpiresActive on
ExpiresDefault A604800
Header set Cache-Control "private, immutable, max-age=604800"
</Directory>
RewriteCond "/opt/GtS/storage/$1" -f
RewriteRule "^/fileserver/(.*)$" "/opt/GtS/storage/$1" [L]
```
The trick here is that, in an Apache 2-based reverse proxy setup…
```
RewriteEngine On
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/?(.*) "ws://localhost:8980/$1" [P,L]
ProxyIOBufferSize 65536
ProxyTimeout 120
ProxyPreserveHost On
<Location "/">
ProxyPass http://127.0.0.1:8980/
ProxyPassReverse http://127.0.0.1:8980/
</Location>
```
… everything is proxied by default, the `RewriteRule` bypasses
the proxy (by specifying a filesystem path to redirect to) for
specific URL præficēs and the `RewriteCond` ensures to only
disable the `/fileserver/` proxy if the file is, indeed, present.
Also run the following commands (assuming a Debian-like setup)
to enable the modules used:
```
$ sudo a2enmod expires
$ sudo a2enmod headers
$ sudo a2enmod rewrite
```
Then (after a configtest), restart Apache.
### nginx
Here's an example of the three location blocks you'll need to add to your existing configuration in nginx:
```nginx
server {
server_name social.example.org;
location /assets/ {
alias web-asset-base-dir/;
autoindex off;
expires 5m;
add_header Cache-Control "public";
}
location @fileserver {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /fileserver/ {
alias storage-local-base-path/;
autoindex off;
expires 1w;
add_header Cache-Control "private, immutable";
try_files $uri @fileserver;
}
}
```
The `/fileserver` location is a bit special. When we fail to fetch the media from disk, we want to proxy the request on to GoToSocial so it can try and fetch it. The `try_files` directive can't take a `proxy_pass` itself so instead we created the named `@fileserver` location that we pass in last to `try_files`.
!!! bug "Trailing slashes"
The trailing slashes in the `location` directives and the `alias` are significant, do not remove those.
The `expires` directive adds the necessary headers to inform the client how long it may cache the resource:
* For assets, which may change on each release, 5 minutes is used in this example
* For attachments, which should never change once they're created, we currently use one week
For other options, see the nginx documentation on the [`expires` directive](https://nginx.org/en/docs/http/ngx_http_headers_module.html#expires).
Nginx does not add cache headers to 4xx or 5xx response codes so a failure to fetch an asset won't get cached by clients. The `autoindex off` directive tells nginx to not serve a directory listing. This should be the default but it doesn't hurt to be explicit. The added `add_header` lines set additional options for the `Cache-Control` header:
* `public` is used to indicate that anyone may cache this resource
* `immutable` is used to indicate this resource will never change while it is fresh (it's before the end of the expires) allowing clients to forgo conditional requests to revalidate the resource during that timespan.

View file

@ -1,11 +0,0 @@
# Caching
This section covers a number of different caching techniques that can be used to make GoToSocial more robust in the face of higher traffic and offload the GoToSocial instance from some work.
!!! note
These guides are only relevant if you're running a [reverse proxy](../../getting_started/reverse_proxy/index.md).
## Guides
* [Caching API responses](api.md)
* [Assets and media serving and caching](assets-media.md)

View file

@ -1,108 +0,0 @@
# Provisioning TLS certificates
As explained in the [deployment considerations](../getting_started/index.md), federation requires the use of TLS as most instances refuse to federate over unencrypted transports.
GoToSocial comes with built-in support for provisioning and renewing its own TLS certificates through Lets Encrypt. This guide looks at how you can provision your own certificates independently from GoToSocial. This can be useful if you want full control over how the certificates are provisioned, or because you're using a [reverse proxy](../getting_started/reverse_proxy/index.md) which is doing TLS termination.
There are a few different ways you can get TLS certificates:
* Buy them from a vendor, typically valid for 2 years
* Get them from your cloud provider, validity depends on their product constraints
* Get them from an [ACME](https://en.wikipedia.org/wiki/Automatic_Certificate_Management_Environment)-compatible provider like Lets Encrypt, typically valid for 3 months at a time
In this guide we'll only look at option 3, an ACME-compatible vendor.
## General approach
The way you'll provision certificates through Lets Encrypt is:
* Install an ACME client on your server
* Configure the ACME client to provision your certificates
* Configure a piece of software to use those certificates
* Enable a timer/cron to regularly renew the certificates
* Signal to the necessary applications they need to reload or restart to pick up the new certificates
Certificates are provisioned [using a challenge](https://letsencrypt.org/sv/docs/challenge-types/), a way to verify that you're requesting a certificate for a domain you control. You'll typically use one of:
* HTTP challenge
* DNS challenge
The HTTP challenge requires serving certain files on port 80 on the domains you're requesting a certificate for under the `/.well-known/acme/` path. This is the default challenge type.
The DNS challenge happens entirely out of band but requires you to update a DNS TXT record. This approach is only feasible if your DNS registrar provides an API through which you can modify DNS records so that your ACME client can complete this challenge.
## Clients
The official Lets Encrypt client is [certbot](https://certbot.eff.org/) and it's usually packaged in [your (Linux) distribution](https://repology.org/project/certbot/versions) of choice. Certain reverse proxies like Caddy and Traefik have built-in support for provisioning certificates using the ACME protocol.
A couple of other clients of note that you can consider using:
* [acme-client](https://man.openbsd.org/acme-client.1) for OpenBSD using the privilege separation features of the platform
* [lacme](https://git.guilhem.org/lacme/about/), which is built with process isolation and minimal privileges in mind in the same vein as acme-client but for Linux
* [Lego](https://github.com/go-acme/lego), an ACME client and library written in Go
* [mod_md](https://httpd.apache.org/docs/2.4/mod/mod_md.html), when using Apache 2.4.30+
### DNS challenge
For the DNS challenge, the API of your registrar needs to be supported by your ACME client. Though certbot has a few plugins for popular providers, you probably want to look at the [dns-multi](https://github.com/alexzorin/certbot-dns-multi) plugin instead. It leverages [Lego](https://github.com/go-acme/lego) under the hood which supports a much wider array of providers.
## Configuration
There are 3 configuration options that are important:
* [`letsencrypt-enabled`](../configuration/tls.md) controls if GoToSocial will try to provision its own certificates
* [`tls-certificate-chain`](../configuration/tls.md) filesystem path where GoToSocial can find the TLS certificate chain + the public key
* [`tls-certificate-key`](../configuration/tls.md) filesystem path where GoToSocial can find the associated TLS private key
### Without reverse proxy
When running GoToSocial directly exposed to the internet, but you still want to use your own certificates you can set the following options:
```yaml
letsencrypt-enabled: false
tls-certificate-chain: "/path/to/combined-certificate-chain-public.key"
tls-certificate-key: "/path/to/private.key"
```
This disables the built-in provisioning of certificates through Lets Encrypt and tells GoToSocial where to find the certificates on disk.
!!! tip
Restart GoToSocial after renewing your certificates. It won't pick up on certificate rotation by itself when they are provided like this.
### With reverse proxy
When running GoToSocial behind a [reverse proxy](../getting_started/reverse_proxy/index.md) which you also use for TLS termination, you'll need the following instead:
```yaml
letsencrypt-enabled: false
tls-certificate-chain: ""
tls-certificate-key: ""
```
It's important to ensure the `tls-certificate-*` options are unset or set to the empty string. Otherwise GoToSocial will attempt to handle TLS itself.
!!! danger "Protocol configuration option"
Do **not** change the [`protocol`](../configuration/general.md) configuration option to `http`. This should only ever by set to `http` for development purposes. It needs to be set to `https` even when running behind a TLS-terminating reverse proxy.
You'll also want to change the [`port`](../configuration/general.md) GoToSocial binds on, so it no longer tries to use port 443.
To configure TLS in your reverse proxy, please refer to their documentation:
* [nginx](https://docs.nginx.com/nginx/admin-guide/security-controls/terminating-ssl-http/)
* [apache](https://httpd.apache.org/docs/2.4/ssl/ssl_howto.html)
* [Traefik](https://doc.traefik.io/traefik/https/tls/)
* [Caddy](https://caddyserver.com/docs/caddyfile/directives/tls)
!!! tip
When configuring TLS in your reverse proxy, ensure you configure a reasonably modern set of compatible versions and ciphers. You can use the "Intermediate" configuration from the [Mozilla SSL Configuration Generator](https://ssl-config.mozilla.org/).
Check the documentation of your reverse proxy on whether you have to reload or restart it after certificates have changed. Not all reverse proxies detect this.
## Guides
There are a number of good resources on the internet explaining how to set all of this up.
* [ArchWiki entry](https://wiki.archlinux.org/title/certbot) on certbot
* [Gentoo wiki entry](https://wiki.gentoo.org/wiki/Let%27s_Encrypt) on Lets Encrypt
* [Linode guide](https://www.linode.com/docs/guides/enabling-https-using-certbot-with-nginx-on-fedora/) on certbot for Fedora, RHEL/CentOS, Debian and Ubuntu
* Digital Ocean guides on Lets Encrypt on Ubuntu 22.04 with [nginx](https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-22-04) or [apache](https://www.digitalocean.com/community/tutorials/how-to-secure-apache-with-let-s-encrypt-on-ubuntu-22-04)

View file

@ -1,104 +0,0 @@
# Split-domain deployments
This guide explains how to have usernames like `@me@example.org` but run the GoToSocial instance itself on a subdomain like `social.example.org`. Configuring this type of deployment layout **must** be done before starting GoToSocial for the first time.
!!! danger
You cannot change your domain layout after you've federated with someone. Servers are going to get confused and you'll need to convince the admin of every instance that's federated with you before to mess with their database to resolve it. It also requires regenerating the database on your side to create a new instance account and pair of encryption keys.
## Background
The way ActivityPub implementations discover how to map your account domain to your host domain is through a protocol called [webfinger](https://www.rfc-editor.org/rfc/rfc7033). This mapping is typically cached by servers and hence why you can't change it after the fact.
It works by doing a request to `https://<account domain>/.well-known/webfinger?resource=acct:@me@example.org`. At this point, a server can return a redirect to where the actual webfinger endpoint is, `https://<host domain>/.well-known/webfinger?resource=acct:@me@example.org` or may respond directly. The JSON document that is returned informs you what the endpoint to query is for the user:
```json
{
"subject": "acct:me@example.org",
"aliases": [
"https://social.example.org/users/me",
"https://social.example.org/@me"
],
"links": [
{
"rel": "http://webfinger.net/rel/profile-page",
"type": "text/html",
"href": "https://social.example.org/@me"
},
{
"rel": "self",
"type": "application/activity+json",
"href": "https://social.example.org/users/me"
}
]
}
```
ActivityPub clients and servers will now use the entry from the `links` array with `rel` `self` and `type` `application/activity+json` to query for further information, like where the `inbox` is located to federated messages to.
## Configuration
There are 2 configuration settings you'll need to concern yourself with:
* `host`, the domain the API will be served on and what clients and servers will end up using when talking to your instance
* `account-domain`, the domain user accounts will be created on
In order to achieve the setup as described in the introduction, you'll need to set these two configuration options accordingly:
```yaml
host: social.example.org
account-domain: example.org
```
!!! info
The `host` must always be the DNS name that your GoToSocial instance runs on. It does not affect the IP address the GoToSocial instance binds to. That is controlled with `bind-address`.
## Reverse proxy
When using a [reverse proxy](../getting_started/reverse_proxy/index.md) you'll need to ensure you're set up to handle traffic on both of those domains. You'll need to redirect a few endpoints from the account domain to the host domain.
Redirects are typically used so that the change of domain can be detected client side. The endpoints to redirect from the account domain to the host domain are:
* `/.well-known/webfinger`
* `/.well-known/host-meta`
* `/.well-known/nodeinfo`
!!! tip
Do not proxy or redirect requests to the API endpoints, `/api/...`, from the account domain to the host domain. This will confuse heuristics some clients use to detect a split-domain deployment resulting in broken login flows and other weird behaviour.
### nginx
In order to configure the redirect, you'll need to configure it on the account domain. Assuming the account domain is `example.org` and the host domain is `social.example.org`, the following configuration snippet showcases how to do this:
```nginx
server {
server_name example.org;
location /.well-known/webfinger {
rewrite ^.*$ https://social.example.org/.well-known/webfinger permanent;
}
location /.well-known/host-meta {
rewrite ^.*$ https://social.example.org/.well-known/host-meta permanent;
}
location /.well-known/nodeinfo {
rewrite ^.*$ https://social.example.org/.well-known/nodeinfo permanent;
}
}
```
### Traefik
If `example.org` is running on [Traefik](https://doc.traefik.io/traefik/), we could use labels similar to the following to setup the redirect.
```yaml
myservice:
image: foo
# Other stuff
labels:
- 'traefik.http.routers.myservice.rule=Host(`example.org`)'
- 'traefik.http.middlewares.myservice-gts.redirectregex.permanent=true'
- 'traefik.http.middlewares.myservice-gts.redirectregex.regex=^https://(.*)/.well-known/(webfinger|nodeinfo|host-meta)$$'
- 'traefik.http.middlewares.myservice-gts.redirectregex.replacement=https://social.$${1}/.well-known/$${2}'
- 'traefik.http.routers.myservice.middlewares=myservice-gts@docker'
```

View file

@ -1,16 +0,0 @@
# Advanced
In this section we touch on a number of more advanced topics, primarily related around deploying, operating and tuning GoToSocial.
We consider these topics advanced because applying them incorrectly does have the possibility of causing client and federation issues. Applying any of these configuration changes may also make it harder for you to debug an issue with your GoToSocial instance if you don't understand the changes that you're making.
## Guides
* [Split-domain deployment (API vs. account domain)](host-account-domain.md)
* [Using an HTTP proxy for client/outgoing requests](outgoing-proxy.md)
* [Provisioning TLS certificates](certificates.md)
* [Caching API responses](caching/api.md)
* [Serving and caching assets and media from local storage](caching/assets-media.md)
* [Process sandboxing](security/sandboxing.md)
* [Firewall configuration](security/firewall.md)
* [Tracing](tracing.md)

View file

@ -1,21 +0,0 @@
# Outgoing HTTP proxy
GoToSocial supports canonical environment variables for configuring the use of an HTTP proxy for outgoing requets:
* `HTTP_PROXY`
* `HTTPS_PROXY`
* `NO_PROXY`
The lowercase versions of these environment variables are also recognised. `HTTPS_PROXY` takes precedence over `HTTP_PROXY` for https requests.
The environment values may be either a complete URL or a `host[:port]`, in which case the "http" scheme is assumed. The schemes "http", "https", and "socks5" are supported.
## systemd
When running with systemd, you can add the necessary environment variables using the `Environment` option in the `Service` section.
How to do so is documented in the [`systemd.exec` manual](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Environment).
## Container runtime
Environment variables can be set in the compose file under the `environment` key. You can also pass them on the CLI to Docker or Podman's `run` command with `-e KEY=VALUE` or `--env KEY=VALUE`.

View file

@ -1,84 +0,0 @@
# Firewall
You should deploy a firewall on your instance to close off any open ports and give you a mechanism to ban potentially misbehaving clients. Many firewall frontends will also automatically install some rules that block obvious malicious packets.
It can be helpful to deploy tools that monitor your log files for certain patterns and automatically ban clients exhibiting certain behaviour. This can be use to monitor your SSH and web server access logs for things like SSH brute-force attacks.
## Ports
For GoToSocial, you'll want to ensure port `443` remains open. Without it, nobody will be able to reach your instance. Federation will fail and client apps won't be able to work at all.
If you [provision TLS certificates](../certificates.md) using ACME or GoToSocial's built-in Lets Encrypt support, you'll also need port `80` to be open.
In order to access your instance over SSH, you'll need to keep the port your SSH daemon is bound on open too. By default this is port `22`.
## ICMP
[Internet Control Message Protocol](https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol) are exchanged between machines in order to detect certain network conditions or troubleshoot things. Many firewalls have a tendency of blocking ICMP entirely but this is undesirable. A few ICMP types should be allowed and you can use your firewall to configure rate limiting for them instead.
### IPv4
In order for things to work reliably, your firewall must allow:
* ICMP Type 3: "Destination Unreachable" and also aids in Path-MTU Discovery
* ICMP Type 4: "Source Quench"
If you want to be able to ping things or be pinged, you should also allow:
* ICMP Type 0: "Echo Reply"
* ICMP Type 8: "Echo Request"
For traceroute to work, it can be helpful to also allow:
* ICMP Type 11: "Time Exceeded"
### IPv6
ICMP is heavily relied on by all parts of the IPv6 stack and things will break in exciting and hard to debug ways if you block it. [RFC 4890](https://www.rfc-editor.org/rfc/rfc4890) was specifically written to address this and is worthwhile to review.
Roughly speaking, you must always allow:
* ICMP Type 1: "Destination Unreachable"
* ICMP Type 2: "Packet Too Big"
* ICMP Type 3, code 0: "Time Exceeded"
* ICMP Type 4, code 1, 2: "Parameter Problem"
For ping, you should allow:
* ICMP Type 128: "Echo Request"
* ICMP Type 129: "Echo Response"
## Firewall configuration
On Linux, firewalling is typically done using either [iptables](https://en.wikipedia.org/wiki/Iptables) or the more modern and faster [nftables](https://en.wikipedia.org/wiki/Nftables) as the backend. Most distributions are switching to nftables and many firewall frontends can be configured to use nftables instead. You'll need to refer to your distribution's documentation on the matter, but typically there will be an `iptables` or `nftables` service your init-system can start with a predefined location to load firewall rules from.
Doing this by hand using raw iptables or nftables rules offers the most control but can be challenging if you're not familiar with these systems. In order to help with that, a number of configuration frontends exist that you can use.
On the Debian and Ubuntu as well as openSUSE family of distributions, UFW is commonly used. It's a simple firewall front-end and many tutorials targeting those distributions will be using it.
For the Red Hat/CentOS family of distributions, firewalld is typically used. It's a much more advanced firewall configuration utility which also has a desktop GUI and [Cockpit integration](https://cockpit-project.org/).
Despite distribution preferences, you can use UFW, firewalld or something else entirely with any Linux distribution.
* [Ubuntu Wiki](https://wiki.ubuntu.com/UncomplicatedFirewall?action=show&redirect=UbuntuFirewall) on UFW
* [ArchWiki](https://wiki.archlinux.org/title/Uncomplicated_Firewall) on UFW
* Digital Ocean guide on [using UFW with Ubuntu 22.04](https://www.digitalocean.com/community/tutorials/how-to-set-up-a-firewall-with-ufw-on-ubuntu-22-04)
* [firewalld](https://firewalld.org/) project homepage and documentation
* [ArchWiki](https://wiki.archlinux.org/title/firewalld) on firewalld
* [Using and configuring firewalld](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html/configuring_firewalls_and_packet_filters/using-and-configuring-firewalld_firewall-packet-filters) from Red Hat
* Linode guide [on using firewalld](https://www.linode.com/docs/guides/introduction-to-firewalld-on-centos/)
## Brute-force protection
[fail2ban](https://www.fail2ban.org) and [SSHGuard](https://www.sshguard.net/) can be set up to monitor your log files for attempts to brute-force logins and other malicious behaviour. They can be configured to automatically insert firewall rules to block malicious IP addresses, either for a specific period of time or even indefinitely.
SSHGuard was initially designed just for SSH, but nowadays supports a variety of services. Fail2ban tends to support anything you can generate consistent log lines for, whereas SSHGuard's signature approach can catch more sophisticated or stealthy attacks as it computes an attack score over time.
Both SSHGuard and fail2ban ship with "backends" that can target iptables and nftables directly, or work with your frontend of choice like UFW or firewalld on Linux or pf on \*BSD. Make sure you review their documentation on how to correctly configure it.
* [ArchWiki](https://wiki.archlinux.org/title/Fail2ban) on fail2ban
* DigitalOcean guide on how to protect SSH with [fail2ban on Ubuntu](https://www.digitalocean.com/community/tutorial_collections/how-to-protect-ssh-with-fail2ban)
* Linode guide on how to [secure your server with fail2ban](https://www.linode.com/docs/guides/using-fail2ban-to-secure-your-server-a-tutorial/)
* [ArchWiki](https://wiki.archlinux.org/title/sshguard) on sshguard
* [FreeBSD manual](https://man.freebsd.org/cgi/man.cgi?query=sshguard&sektion=8&manpath=FreeBSD+13.2-RELEASE+and+Ports) for sshguard
* [SSHGuard setup](https://manpages.ubuntu.com/manpages/lunar/en/man7/sshguard-setup.7.html) manual for Ubuntu

View file

@ -1,11 +0,0 @@
# Enhanced security
These guides cover improving the security posture of your GoToSocial deployment. They don't involve tweaking settings in GoToSocial, but rather additional things you can do to better lock down your instance.
!!! note
Anything in these guides is meant to enhance the security of your GoToSocial deployment; they are not a replacement for good security practices like keeping your systems patched and up to date.
## Guides
* [Sandboxing the GoToSocial binary](sandboxing.md)
* [Firewall configuration](firewall.md)

View file

@ -1,78 +0,0 @@
# Application sandboxing
By sandboxing the GoToSocial binary it's possible to control which parts of the system GoToSocial can access, and limit which things it can read and write. This can be helpful to ensure that even in the face of a security issue in GoToSocial, an attacker is severely hindered in escalating their privileges and gaining a foothold on your system.
!!! note
As GoToSocial is still early in its development, the sandboxing policies we ship may get out of date. If you happen to run into this, please raise an issue on the issue tracker or better yet submit a PR to help us fix it.
Different distributions have different sandboxing mechanisms they prefer and support:
* **AppArmor** for the Debian or Ubuntu family of distributions or OpenSuSE, including when running with Docker
* **SELinux** for the Red Hat/Fedora/CentOS family of distributions or Gentoo
!!! warning "Containers and sandboxing"
Running GoToSocial as a container does not in and of itself provide much additional security. Despite their name, "containers do not contain". Containers are a distribution mechanism, not a security sandbox. To further secure your container you can instruct the container runtime to load the AppArmor profile and look into limiting which syscalls can be used using a seccomp profile.
## AppArmor
We ship an example AppArmor policy for GoToSocial, which you can retrieve and install as follows:
```sh
$ curl -LO 'https://github.com/superseriousbusiness/gotosocial/raw/main/example/apparmor/gotosocial'
$ sudo install -o root -g root gotosocial /etc/apparmor.d/gotosocial
$ sudo apparmor_parser -Kr /etc/apparmor.d/gotosocial
```
!!! tip
If you're using SQLite, the AppArmor profile expects the database in `/gotosocial/db/` so you'll need to adjust your configuration paths or the policy accordingly.
With the policy installed, you'll need to configure your system to use it to constrain the permissions GoToSocial has.
You can disable the policy like this:
```sh
$ sudo apparmor_parser -R /etc/apparmor.d/gotosocial
$ sudo rm -vi /etc/apparmor.d/gotosocial
```
Don't forget to roll back any configuration changes you made that load the AppArmor policy.
### systemd
Add the following to the systemd service, or create an override:
```ini
[Service]
...
AppArmorProfile=gotosocial
```
Reload systemd and restart GoToSocial:
```
$ systemctl daemon-reload
$ systemctl restart gotosocial
```
### Containers
!!! tip
You should review the [Docker](https://docs.docker.com/engine/security/apparmor/) or [Podman](https://docs.podman.io/en/latest/markdown/options/security-opt.html) documentation on AppArmor.
When using our example Compose file, you can tell it to load the AppArmor policy by tweaking it like so:
```yaml
services:
gotosocial:
...
security_opt:
- apparmor=gotosocial
```
When launching the container with `docker run` or `podman run`, you'll need the `--security-opt="apparmor=gotosocial"` command line flag.
## SELinux
!!! note
SELinux can only be used in combination with the [binary installation](../../getting_started/installation/metal.md) method. SELinux cannot be used to constrain GoToSocial when running in a container.
The SELinux policy is maintained by the community in the [`lzap/gotosocial-selinux`](https://github.com/lzap/gotosocial-selinux) repository on GitHub. Make sure to read its documentation, review the policy before using it and use their issue tracker for any support requests around the SELinux policy.

View file

@ -1,44 +0,0 @@
# Tracing
GoToSocial comes with [OpenTelemetry][otel] based tracing built-in. It's not wired through every function, but our HTTP handlers and database library will create spans. How to configure tracing is explained in the [Observability configuration reference][obs].
In order to receive the traces, you need something to ingest them and then visualise them. There are many options available including self-hosted and commercial options.
We provide an example of how to do this using [Grafana Tempo][tempo] to ingest the spans and [Grafana][grafana] to explore them. Please beware that the configuration we provide is not suitable for a production setup. It can be used safely for local development and can provide a good starting point for setting up your own tracing infrastructure.
You'll need the files in [`example/tracing`][ext]. Once you have those you can run `docker-compose up -d` to get Tempo and Grafana running. With both services running, you can add the following to your GoToSocial configuration and restart your instance:
```yaml
tracing-enabled: true
tracing-transport: "grpc"
tracing-endpoint: "localhost:4317"
tracing-insecure: true
```
[otel]: https://opentelemetry.io/
[obs]: ../configuration/observability.md
[tempo]: https://grafana.com/oss/tempo/
[grafana]: https://grafana.com/oss/grafana/
[ext]: https://github.com/superseriousbusiness/gotosocial/tree/main/example/tracing
## Querying and visualising traces
Once you execute a few queries against your instance, you'll be able to find them in Grafana. You can use the Explore tab and pick Tempo as the datasource. Because our example configuration for Grafana enables [TraceQL][traceql], the Explore tab will have the TraceQL query type selected by default. You can switch to "Search" instead and find all traces emitted by GoToSocial under the "GoToSocial" service name.
Using TraceQL, a simple query to find all traces related to requests to `/api/v1/instance` would look like this:
```
{.http.route = "/api/v1/instance"}
```
If you wanted to see all GoToSocial traces, you could instead run:
```
{.service.name = "GoToSocial"}
```
Once you select a trace, a second panel will open up visualising the span. You can drill down from there, by clicking into every sub-span to see what it was doing.
![Grafana showing a trace for the /api/v1/instance endpoint](../assets/tracing.png)
[traceql]: https://grafana.com/docs/tempo/latest/traceql/

View file

@ -6,4 +6,4 @@ The resulting API documentation is rendered below, for quick reference.
If you'd like to do more with the spec, you can also view the [swagger.yaml](/api/swagger/swagger.yaml) directly, and then paste it into something like the [Swagger Editor](https://editor.swagger.io/) in order to autogenerate GoToSocial API clients in different languages, convert the doc to JSON or OpenAPI v3 specification, etc. See [here](https://swagger.io/tools/open-source/getting-started/) for more.
<swagger-ui src="swagger.yaml"/>
!!swagger swagger.yaml!!

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

BIN
docs/assets/admin-panel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

View file

@ -1,25 +0,0 @@
[data-md-color-scheme="slate"] {
--md-primary-fg-color: #fd6a00;
--md-default-fg-color: #fafaff;
--md-default-fg-color--light: var(--md-default-fg-color);
--md-default-fg-color--lighter: var(--md-default-fg-color);
--md-default-fg-color--lightest: var(--md-default-fg-color);
--md-default-bg-color: #2a2b2f;
--md-default-bg-color--light: #35363b;
--md-default-bg-color--lighter: #3a3b41;
--md-default-bg-color--lightest: #4d4e56;
--md-typeset-color: var(--md-default-fg-color);
--md-typeset-a-color: #66befe;
--md-accent-fg-color: #89caff;
--md-footer-bg-color: var(--md-default-bg-color);
}
[data-md-color-scheme="slate"] .md-nav--primary .md-nav__title {
background: var(--md-default-bg-color) !important;
}
.md-content {
background: var(--md-default-bg-color--lightest);
}

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 123 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 879 KiB

After

Width:  |  Height:  |  Size: 627 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 673 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 266 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

After

Width:  |  Height:  |  Size: 400 KiB

View file

@ -39,11 +39,4 @@ accounts-reason-required: true
# Options: [true, false]
# Default: false
accounts-allow-custom-css: false
# Int. If accounts-allow-custom-css is true, this is the permitted length in characters for
# CSS uploaded by accounts on this instance. No effect if accounts-allow-custom-css is false.
#
# Examples: [500, 5000, 9999]
# Default: 10000
accounts-custom-css-length: 10000
```

View file

@ -53,7 +53,7 @@ advanced-cookies-samesite: "lax"
advanced-rate-limit-requests: 300
# Int. Amount of open requests to permit per CPU, per router grouping, before applying http
# request throttling. Any requests beyond the calculated limit are held in a backlog queue for
# request throttling. Any requests beyond the calculated limit are held in a backlog queue for
# up to 30 seconds before either being processed or timing out. Requests that don't fit in the backlog
# queue will have status 503 returned to them, and the header 'Retry-After' will be set to 30 seconds.
#
@ -71,7 +71,7 @@ advanced-rate-limit-requests: 300
# 2 cpu = 08 open, 032 backlog
# 4 cpu = 16 open, 064 backlog
#
# A multiplier of 8 is a sensible default, but you may wish to increase this for instances
# A multiplier of 8 is a sensible default, but you may wish to increase this for instances
# running on very performant hardware, or decrease it for instances using v. slow CPUs.
#
# If you set this to 0 or less, http request throttling will be disabled entirely.
@ -79,43 +79,4 @@ advanced-rate-limit-requests: 300
# Examples: [8, 4, 9, 0]
# Default: 8
advanced-throttling-multiplier: 8
# Duration. Time period to use as the "retry-after" header value in response to throttled requests.
# Minimum resolution is 1 second.
#
# Examples: [30s, 10s, 5s, 1m]
# Default: "30s"
advanced-throttling-retry-after: "30s"
# Int. CPU multiplier for the amount of goroutines to spawn in order to send messages via ActivityPub.
# Messages will be batched so that at most multiplier * CPU count messages will be sent out at once.
# This can be tuned to limit concurrent POSTing to remote inboxes, preventing your instance CPU
# usage from skyrocketing when an account with many followers posts a new status.
#
# Messages are split among available senders, and each sender processes its assigned messages in serial.
# For example, say a user with 1000 followers is on an instance with 2 CPUs. With the default multiplier
# of 2, this means 4 senders would be in process at once on this instance. When the user creates a new post,
# each sender would end up iterating through about 250 Create messages + delivering them to remote instances.
#
# If you set this to 0 or less, only 1 sender will be used regardless of CPU count. This may be
# useful in cases where you are working with very tight network or CPU constraints.
#
# Example values for multiplier 2 (default):
#
# 1 cpu = 2 concurrent senders
# 2 cpu = 4 concurrent senders
# 4 cpu = 8 concurrent senders
#
# Example values for multiplier 4:
#
# 1 cpu = 4 concurrent senders
# 2 cpu = 8 concurrent senders
# 4 cpu = 16 concurrent senders
#
# Example values for multiplier <1:
#
# 1 cpu = 1 concurrent sender
# 2 cpu = 1 concurrent sender
# 4 cpu = 1 concurrent sender
advanced-sender-multiplier: 2
```

View file

@ -107,64 +107,4 @@ db-tls-mode: "disable"
# Examples: ["/path/to/some/cert.crt"]
# Default: ""
db-tls-ca-cert: ""
# Int. Number to multiply by CPU count to set permitted total of open database connections (in-use and idle).
# You can use this setting to tune your database connection behavior, though most admins won't need to touch it.
#
# Example values for multiplier 8:
#
# 1 cpu = 08 open connections
# 2 cpu = 16 open connections
# 4 cpu = 32 open connections
#
# Example values for multiplier 4:
#
# 1 cpu = 04 open connections
# 2 cpu = 08 open connections
# 4 cpu = 16 open connections
#
# A multiplier of 8 is a sensible default, but you may wish to increase this for instances
# running on very performant hardware, or decrease it for instances using v. slow CPUs.
#
# If you set the multiplier to less than 1, only one open connection will be used regardless of cpu count.
#
# PLEASE NOTE!!: This setting currently only applies for Postgres. SQLite will always use 1 connection regardless
# of what is set here. This behavior will change in future when we implement better SQLITE_BUSY handling.
# See https://github.com/superseriousbusiness/gotosocial/issues/1407 for more details.
#
# Examples: [16, 8, 10, 2]
# Default: 8
db-max-open-conns-multiplier: 8
# String. SQLite journaling mode.
# SQLite only -- unused otherwise.
# If set to empty string, the sqlite default will be used.
# See: https://www.sqlite.org/pragma.html#pragma_journal_mode
# Examples: ["DELETE", "TRUNCATE", "PERSIST", "MEMORY", "WAL", "OFF"]
# Default: "WAL"
db-sqlite-journal-mode: "WAL"
# String. SQLite synchronous mode.
# SQLite only -- unused otherwise.
# If set to empty string, the sqlite default will be used.
# See: https://www.sqlite.org/pragma.html#pragma_synchronous
# Examples: ["OFF", "NORMAL", "FULL", "EXTRA"]
# Default: "NORMAL"
db-sqlite-synchronous: "NORMAL"
# Byte size. SQlite cache size.
# SQLite only -- unused otherwise.
# If set to empty string or zero, the sqlite default (2MiB) will be used.
# See: https://www.sqlite.org/pragma.html#pragma_cache_size
# Examples: ["0", "2MiB", "8MiB", "64MiB"]
# Default: "8MiB"
db-sqlite-cache-size: "8MiB"
# Duration. SQlite busy timeout.
# SQLite only -- unused otherwise.
# If set to empty string or zero, the sqlite default will be used.
# See: https://www.sqlite.org/pragma.html#pragma_busy_timeout
# Examples: ["0s", "1s", "30s", "1m", "5m"]
# Default: "30m"
db-sqlite-busy-timeout: "30m"
```

View file

@ -23,21 +23,11 @@ log-level: "info"
# Default: false
log-db-queries: false
# Bool. Include the client IP in the emitted log lines
# Options: [true, false]
# Default: true
log-client-ip: true
# String. Application name to use internally.
# Examples: ["My Application","gotosocial"]
# Default: "gotosocial"
application-name: "gotosocial"
# String. The user that will be shown instead of the landing page. if no user is set, the landing page will be shown.
# Examples: "admin"
# Default: ""
landing-page-user: ""
# String. Hostname that this server will be reachable at. Defaults to localhost for local testing,
# but you should *definitely* change this when running for real, or your server won't work at all.
# DO NOT change this after your server has already run once, or you will break things!
@ -53,11 +43,6 @@ host: "localhost"
# to "gts.example.org/.well-known/webfinger" so that GtS can handle them properly.
#
# You should also redirect requests at "example.org/.well-known/nodeinfo" in the same way.
#
# You should also redirect requests at "example.org/.well-known/host-meta" in the same way. This endpoint
# is used by a number of clients to discover the API endpoint to use when the host and account domain are
# different.
#
# An empty string (ie., not set) means that the same value as 'host' will be used.
#
# DO NOT change this after your server has already run once, or you will break things!

View file

@ -1,68 +0,0 @@
# HTTP Client
## Settings
```yaml
################################
##### HTTP CLIENT SETTINGS #####
################################
# Settings for OUTGOING http client connections used by GoToSocial to make
# requests to remote resources (status GETs, media GETs, inbox POSTs, etc).
http-client:
# Duration. Timeout to use for outgoing HTTP requests. If the timeout
# is exceeded, the connection to the remote server will be dropped.
# A value of 0s indicates no timeout: this is not advised!
# Examples: ["5s", "10s", "0s"]
# Default: "10s"
timeout: "10s"
########################################
#### RESERVED IP RANGE EXCEPTIONS ######
########################################
#
# Explicitly allow or block outgoing dialing within the provided IPv4/v6 CIDR ranges.
#
# By default, as a basic security precaution, GoToSocial blocks outgoing dialing within most "special-purpose"
# IP ranges. However, it may be desirable for admins with more exotic setups (proxies, funky NAT, etc) to
# explicitly override one or more of these otherwise blocked ranges.
#
# Each of the below allow/block config options accepts an array of IPv4 and/or IPv6 CIDR strings.
# For example, to override the hardcoded block of IPv4 and IPv6 dialing to localhost, set:
#
# allow-ips: ["127.0.0.1/32", "::1/128"].
#
# You can also use YAML multi-line arrays to define these, but be diligent with indentation.
#
# When dialing, GoToSocial will first check if the destination falls within explicitly allowed IP ranges,
# then explicitly blocked IP ranges, then the default (hardcoded) blocked IP ranges, returning OK on the
# first allowed match, not OK on the first blocked match, or just defaulting to OK if nothing is matched.
#
# As with all security settings, it is better to start too restrictive and then ease off depending on
# your use case, than to start too permissive and try to close the stable door after the horse has
# already bolted. With this in mind:
# - Don't touch these settings unless you have a good reason to, and only if you know what you're doing.
# - When adding explicitly allowed exceptions, use the narrowest possible CIDR for your use case.
#
# For reserved / special ranges, see:
# - https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
# - https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
#
# Both allow-ips and block-ips default to an empty array.
allow-ips: []
block-ips: []
# Bool. Disable verification of TLS certificates of remote servers.
# With this set to 'true', GoToSocial will not error when a remote
# server presents an invalid or self-signed certificate.
#
# THIS SETTING SHOULD BE USED FOR TESTING ONLY! IF YOU TURN THIS
# ON WHILE RUNNING IN PRODUCTION YOU ARE LEAVING YOUR SERVER WIDE
# OPEN TO MAN IN THE MIDDLE ATTACKS! DO NOT CHANGE THIS SETTING
# UNLESS YOU KNOW EXACTLY WHAT YOU'RE DOING AND WHY YOU'RE DOING IT.
#
# Default: false
tls-insecure-skip-verify: false
```

View file

@ -24,12 +24,6 @@ instance-expose-peers: false
# Default: false
instance-expose-suspended: false
# Bool. Allow unauthenticated users to view /about/suspended,
# showing the HTML rendered list of instances that this instance blocks/suspends.
# Options: [true, false]
# Default: false
instance-expose-suspended-web: false
# Bool. Allow unauthenticated users to make queries to /api/v1/timelines/public in order
# to see a list of public posts on this server. Even if set to 'false', then authenticated
# users (members of the instance) will still be able to query the endpoint.

View file

@ -1,12 +1,4 @@
# TLS
It's possible to configure TLS support in one of two ways:
* Built-in support for Lets Encrypt / ACME compatible vendors
* Loading TLS files from disk
It is not possible to have both methods enabled at the same time.
Note that when using TLS files loaded from disk you are responsible for restarting the instance when the files change. They are not automatically reloaded.
# LetsEncrypt
## Settings
@ -47,20 +39,4 @@ letsencrypt-cert-dir: "/gotosocial/storage/certs"
# Examples: ["admin@example.org"]
# Default: ""
letsencrypt-email-address: ""
##############################
##### MANUAL TLS CONFIG #####
##############################
# String. Path to a PEM-encoded file on disk that includes the certificate chain
# and the public key
# Examples: ["/gotosocial/storage/certs/chain.pem"]
# Default: ""
tls-certificate-chain: ""
# String. Path to a PEM-encoded file on disk containing the private key for the
# associated tls-certificate-chain
# Examples: ["/gotosocial/storage/certs/private.pem"]
# Default: ""
tls-certificate-key: ""
```

View file

@ -7,7 +7,7 @@
##### MEDIA CONFIG #####
########################
# Config pertaining to media uploads (videos, image, image descriptions, emoji).
# Config pertaining to user media uploads (videos, image, image descriptions).
# Int. Maximum allowed image upload size in bytes.
# Examples: [2097152, 10485760]
@ -37,8 +37,8 @@ media-description-max-chars: 500
#
# If this is set to 0, then media from remote instances will be cached indefinitely.
# Examples: [30, 60, 7, 0]
# Default: 7
media-remote-cache-days: 7
# Default: 30
media-remote-cache-days: 30
# Int. Max size in bytes of emojis uploaded to this instance via the admin API.
# The default is the same as the Mastodon size limit for emojis (50kb), which allows
@ -53,6 +53,6 @@ media-emoji-local-max-size: 51200
# This strikes a good balance between decent interoperability with instances that have
# higher emoji size limits, and not taking up too much space in storage.
# Examples: [51200, 102400]
# Default: 102400
# Default: 51200
media-emoji-remote-max-size: 102400
```

View file

@ -1,38 +0,0 @@
# Observability
These settings let you tune and configure certain observability related behaviours.
## Settings
```yaml
##################################
##### OBSERVABILITY SETTINGS #####
##################################
# String. Header name to use to extract a request or trace ID from. Typically set by a
# loadbalancer or proxy.
# Default: "X-Request-Id"
request-id-header: "X-Request-Id"
# Bool. Enable OpenTelemetry based tracing support.
# Default: false
tracing-enabled: false
# String. Set the transport protocol for the tracing system. Can either be "grpc" for
# OTLP gRPC or "jaeger" for jaeger based ingesters.
# Options: ["grpc", "jaeger"]
# Default: "grpc"
tracing-transport: "grpc"
# String. Endpoint of the trace ingester. When using the gRPC based transport, the
# endpoint is usually a single address/port combination. For the jaeger transport it
# should be a fully qualified URL.
# OTLP gRPC or "jaeger" for jaeger based ingesters
# Examples: ["localhost:4317", "http://localhost:14268/api/traces"]
# Default: ""
tracing-endpoint: ""
# Bool. Disable HTTPS for the gRPC transport protocol.
# Default: false
tracing-insecure-transport: false
```

View file

@ -10,9 +10,6 @@ This is very convenient in the following cases:
- You want to delegate management of users, accounts, passwords etc. to an external service to make admin easier for yourself.
- You already have a lot of users in an external system and you don't want to have to recreate them all in GoToSocial manually.
!!! tip
If a user doesn't exist yet, login will fail if your IdP doesn't return a non-empty `email` as part of the claims. The email needs to be unique on this instance. Though we use the `sub` claim to associate the external identity with a GtS user, when a user is created it needs an email associated with it.
## Settings
GoToSocial exposes the following configuration settings for OIDC, shown below with their default values.
@ -63,7 +60,7 @@ oidc-client-secret: ""
# Array of string. Scopes to request from the OIDC provider. The returned values will be used to
# populate users created in GtS as a result of the authentication flow. 'openid' and 'email' are required.
# 'profile' is used to extract a username for the newly created user.
# 'groups' is optional and can be used to determine if a user is an admin based on oidc-admin-groups.
# 'groups' is optional and can be used to determine if a user is an admin (if they're in the group 'admin' or 'admins').
# Examples: See eg., https://auth0.com/docs/scopes/openid-connect-scopes
# Default: ["openid", "email", "profile", "groups"]
oidc-scopes:
@ -78,11 +75,6 @@ oidc-scopes:
# Options: [true, false]
# Default: false
oidc-link-existing: false
# Array of string. If the returned ID token contains a 'groups' claim that matches one of the
# groups in oidc-admin-groups, then this user will be granted admin rights on the GtS instance
# Default: []
oidc-admin-groups: []
```
## Behavior
@ -109,7 +101,7 @@ access to your GtS account.
Most OIDC providers allow for the concept of groups and group memberships in returned claims. GoToSocial can use group membership to determine whether or not a user returned from an OIDC flow should be created as an admin account or not.
If the returned OIDC groups information for a user contains membership of the groups configured in `oidc-admin-groups`, then that user will be created/signed in as though they are an admin.
If the returned OIDC groups information for a user contains membership of the groups `admin` or `admins`, then that user will be created/signed in as though they are an admin.
## Migrating from old versions

View file

@ -45,18 +45,6 @@ smtp-password: ""
# Examples: ["mail@example.org"]
# Default: ""
smtp-from: ""
# Bool. If true, when an email is sent that has multiple recipients, each recipient
# will be included in the To field, so that each recipient can see who else got the
# email, and they can 'reply all' to the other recipients if they want to.
#
# If false, email will be sent to Undisclosed Recipients, and each recipient will not
# be able to see who else received the email.
#
# It might be useful to change this setting to 'true' if you want to be able to discuss
# new moderation reports with other admins by 'replying-all' to the notification email.
# Default: false
smtp-disclose-recipients: false
```
Note that if you don't set `Host`, then email sending via smtp will be disabled, and the other settings will be ignored. GoToSocial will still log (at trace level) emails that *would* have been sent if smtp was enabled.
@ -71,19 +59,11 @@ The exception to this requirement is if you're running your mail server (or brid
### When are emails sent?
Currently, emails are sent:
- To the provided email address of a new user to request email confirmation when a new account is created via the API.
- To all active instance moderators + admins when a new moderation report is received. By default, recipients are Bcc'd, but you can change this behavior with the setting `smtp-disclose-recipients`.
- To the creator of a report (on this instance) when the report is closed by a moderator.
### Can I test if my SMTP configuration is correct?
Yes, you can use the API to send a test email to yourself. Check the API documentation for the `/api/v1/admin/email/test` endpoint.
Currently, emails are only sent to users to request email confirmation when a new account is created, or to serve password reset requests. More email functionality will probably be added later.
### HTML versus Plaintext
Emails are sent in plaintext by default. At this point, there is no option to send emails in html, but this is something that might be added later if there's enough demand for it.
Emails are sent in HTML by default. At this point, there is no option to send emails in plaintext, but this is something that might be added later if there's enough demand for it.
## Customization

View file

@ -24,9 +24,11 @@ storage-local-base-path: "/gotosocial/storage"
# String. API endpoint of the S3 compatible service.
# Only required when running with the s3 storage backend.
#
# If your endpoint contains the bucket name, all files will be put into a
# subdirectory with the name of `storage-s3-bucket`
#
# Examples: ["minio:9000", "s3.nl-ams.scw.cloud", "s3.us-west-002.backblazeb2.com"]
# GoToSocial uses "DNS-style" when accessing buckets.
# If you are using Scaleways object storage, please remove the "bucket name" from the endpoint address
# Default: ""
storage-s3-endpoint: ""
@ -35,27 +37,18 @@ storage-s3-endpoint: ""
# Default: false
storage-s3-proxy: false
# Bool. Use SSL for S3 connections.
#
# Only set this to 'false' when testing locally.
#
# Default: true
storage-s3-use-ssl: true
# String. Access key part of the S3 credentials.
# Consider setting this value using environment variables to avoid leaking it via the config file
# Only required when running with the s3 storage backend.
# Examples: ["AKIAJSIE27KKMHXI3BJQ","miniouser"]
# Default: ""
storage-s3-access-key: ""
# String. Secret key part of the S3 credentials.
# Consider setting this value using environment variables to avoid leaking it via the config file
# Only required when running with the s3 storage backend.
# Examples: ["5bEYu26084qjSFyclM/f2pz4gviSfoOg+mFwBH39","miniopassword"]
# Default: ""
storage-s3-secret-key: ""
# String. Name of the storage bucket.
#
# If you have already encoded your bucket name in the storage-s3-endpoint, this
@ -69,62 +62,6 @@ storage-s3-secret-key: ""
storage-s3-bucket: ""
```
### AWS S3 Bucket Configuration
#### Bucket Created
GoToSocial by default creates signed URL's which means we dont need to change anything major on the policies of the bucket.
Here are the steps to follow for bucket creation
1. Login to AWS -> select S3 as service.
2. click Create Bucket
3. Provide a unique name and avoid adding "." in the name
4. Do not change the public access settings (Let them be on "block public access" mode)
#### AWS ACCESS KEY Configuration
1. In AWS Console -> IAM (under Security, Identity, & Compliance)
2. Add a user with programatic api's access
3. We recommend setting up below listed policy, replace <bucketname> with your buckets name
```json
{
"Statement": [
{
"Effect": "Allow",
"Action": "s3:ListAllMyBuckets",
"Resource": "arn:aws:s3:::*"
},
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::<bucket_name>",
"arn:aws:s3:::<bucket_name>/*"
]
}
]
}
```
4. Provide the values in config above
* storage-s3-endpoint -> should be your bucket location say `s3.ap-southeast-1.amazonaws.com`
* storage-s3-access-key -> Access key you obtained for the user created above
* storage-s3-secret-key -> Secret key you obtained for the user created above
* storage-s3-bucket -> Keep this as the <bucketname> that you created just now.
#### Migrating data from local storage to AWS s3 bucket
This step is only needed if you have a running instance. Ignore this if you are setting up a fresh instance.
We have provided [s3cmd](https://github.com/s3tools/s3cmd) command for the copy operation.
```bash
s3cmd sync --add-header="Cache-Control:public, max-age=315576000, immutable" ./ s3://<bucket name>
```
### Migrating between backends
Currently, migration between backends is freely possible. To do so, you only

View file

@ -32,7 +32,7 @@ syslog-enabled: false
# String. Protocol to use when directing logs to syslog. Leave empty to connect to local syslog.
# Options: ["udp", "tcp", ""]
# Default: "udp"
# Default: "tcp"
syslog-protocol: "udp"
# String. Address:port to send syslog logs to. Leave empty to connect to local syslog.

View file

@ -1,14 +1,45 @@
name: gotosocial-docs
channels:
- conda-forge
- nodefaults
- defaults
dependencies:
- python=3.11.3=h2755cc3_0_cpython
- pip=23.1.2=pyhd8ed1ab_0
- mkdocs=1.4.3=pyhd8ed1ab_0
- mkdocs-material=9.1.9=pyhd8ed1ab_0
- mkdocs-material-extensions=1.1.1=pyhd8ed1ab_0
- pillow=9.5.0=py311h573f0d3_0
- cairosvg=2.6.0=pyhd8ed1ab_0
- _libgcc_mutex=0.1=main
- _openmp_mutex=4.5=1_gnu
- bzip2=1.0.8=h7b6447c_0
- ca-certificates=2021.10.26=h06a4308_2
- certifi=2020.6.20=pyhd3eb1b0_3
- ld_impl_linux-64=2.35.1=h7274673_9
- libffi=3.3=he6710b0_2
- libgcc-ng=9.3.0=h5101ec6_17
- libgomp=9.3.0=h5101ec6_17
- libstdcxx-ng=9.3.0=hd4cf53a_17
- libuuid=1.0.3=h7f8727e_2
- ncurses=6.3=h7f8727e_2
- openssl=1.1.1l=h7f8727e_0
- python=3.10.0=h151d27f_3
- readline=8.1=h27cfd23_0
- sqlite=3.36.0=hc218d9a_0
- tk=8.6.11=h1ccaba5_0
- tzdata=2021e=hda174b7_0
- wheel=0.37.0=pyhd3eb1b0_1
- xz=5.2.5=h7b6447c_0
- zlib=1.2.11=h7b6447c_3
- pip:
- mkdocs-swagger-ui-tag==0.6.1
- click==8.0.3
- ghp-import==2.0.2
- importlib-metadata==4.8.2
- jinja2==3.0.3
- markdown==3.3.4
- markupsafe==2.0.1
- mergedeep==1.3.4
- mkdocs==1.2.3
- mkdocs-render-swagger-plugin==0.0.3
- packaging==21.2
- pip==21.2.4
- pyparsing==2.4.7
- python-dateutil==2.8.2
- pyyaml==6.0
- pyyaml-env-tag==0.1
- setuptools==58.0.4
- six==1.16.0
- watchdog==2.1.6
- zipp==3.6.0

View file

@ -1,50 +1,32 @@
# Frequently Asked Questions
## Where's the user interface?
- **Where's the user interface?** GoToSocial is just a bare server for the most part and is designed to be used thru external applications. [Pinafore](https://pinafore.social) and [Tusky](https://tusky.app/) are the best-supported, but anything that supports the Mastodon API should work, other than the features GoToSocial doesn't yet have. Permalinks and profile pages are served directly thru GoToSocial as well as the admin panel, but most interaction goes thru the apps.
GoToSocial is just a bare server for the most part and is designed to be used thru external applications. [Semaphore](https://semaphore.social/) and [Tusky](https://tusky.app/) are the best-supported, but anything that supports the Mastodon API should work, other than the features GoToSocial doesn't yet have. Permalinks and profile pages are served directly through GoToSocial as well as the settings panel, but most interaction goes through the apps.
- **What happened to the gifs?** While GoToSocial supports gifs, it doesn't support videos. This wouldn't be a big problem, except that Mastodon doesn't support gifs; it converts them into videos when they get uploaded. So if someone posts a gif from a Mastodon server, it won't be visible. At the time of this writing, the video will be dropped altogether, but [in the future there should be at least a placeholder link](https://github.com/superseriousbusiness/gotosocial/issues/765).
## Why aren't my posts showing up on my profile page?
- **Why aren't my posts showing up on my profile page?** Unlike Mastodon, the default post visibility is Unlisted. If you want something to be visible on your profile page, the post must have Public visibility.
Unlike Mastodon, the default post visibility is Unlisted. If you want something to be visible on your profile page, the post must have Public visibility.
- **Why aren't my posts showing up on other servers?** First check the visibility as noted above. TODO: explain how to debug common federation issues
## Why aren't my posts showing up on other servers?
- **Why am I getting frequent http 429 error responses?** GoToSocial is configured to use per-IP [rate limiting](./api/ratelimiting.md) by default, but in certain situations it can't accurately identify the remote IP and will treat all connections as coming from the same place. In those cases, the rate limiting needs to be disabled or reconfigured.
First check the visibility as noted above. TODO: explain how to debug common federation issues
- **Why am I getting frequent http 503 error responses?** Code 503 is returned to callers when your instance is under heavy load and requests are being throttled. This behavior can be tuned as desired, or turned off entirely, see [here](./api/throttling.md).
## Why am I getting frequent http 429 error responses?
- **My instance is deployed and I'm logged in to a client but my timelines are empty, what's up there?** To see posts, you have to start following people! Once you've followed a few people and they've posted or boosted things, you'll start seeing them in your timelines. Right now GoToSocial doesn't have a way of 'backfilling' posts -- that is, fetching previous posts from other instances -- so you'll only see new posts of people you follow. If you want to interact with an older post of theirs, you can copy the link to the post from their web profile, and paste it in to your client's search bar.
GoToSocial is configured to use per-IP [rate limiting](./api/ratelimiting.md) by default, but in certain situations it can't accurately identify the remote IP and will treat all connections as coming from the same place. In those cases, the rate limiting needs to be disabled or reconfigured.
- **How can I sign up for a server?** Right now the only way to create an account is by the server's admin to run a command directly on the server. A web-based signup flow is in the roadmap but not implemented yet.
## Why am I getting frequent HTTP 503 error responses?
Code 503 is returned to callers when your instance is under heavy load and requests are being throttled. This behavior can be tuned as desired, or turned off entirely, see [here](./api/throttling.md).
## I keep getting a 400 Bad Request error, and I have done everything suggested by the error message. What should I do?
Verify that the `host` configuration matches the domain that GoToSocial is served from (the domain that users use to acces the server).
## I keep seeing 'dial within blocked / reserved IP range' in my server logs, and I can't connect to some instances from my instance, what do I do?
The IP address of the remote instance may be in one of the blocked "special use" IP ranges hardcoded into GoToSocial for security reasons. If you need to, you can override this in your configuration file. Have a look at the [http client docs](./configuration/httpclient.md) for this, and please read the warnings there carefully! If you add an explicit allow, you will have to restart your GoToSocial instance to make the config take effect.
## My instance is deployed and I'm logged in to a client but my timelines are empty, what's up there?
To see posts, you have to start following people! Once you've followed a few people and they've posted or boosted things, you'll start seeing them in your timelines. Right now GoToSocial doesn't have a way of 'backfilling' posts -- that is, fetching previous posts from other instances -- so you'll only see new posts of people you follow. If you want to interact with an older post of theirs, you can copy the link to the post from their web profile, and paste it in to your client's search bar.
## How can I sign up for a server?
Right now the only way to create an account is by the server's admin to run a command directly on the server. A web-based signup flow is in the roadmap but not implemented yet.
## Why's it still in alpha?
Take a look at the [list of open bugs](https://github.com/superseriousbusiness/gotosocial/issues?q=is%3Aissue+is%3Aopen+label%3Abug) and the [roadmap](https://github.com/superseriousbusiness/gotosocial/blob/main/ROADMAP.md) for a more detailed rundown, but the main missing features at the time of this writing are:
- muting conversations
- backfill of posts
- web-based signup
- polls
- scheduling posts
- account migration
- federated hashtag search
- shared block lists across servers
- **Why's it still in alpha?** Take a look at the [list of open bugs](https://github.com/superseriousbusiness/gotosocial/issues?q=is%3Aissue+is%3Aopen+label%3Abug) and the [roadmap](https://github.com/superseriousbusiness/gotosocial/blob/main/ROADMAP.md) for a more detailed rundown, but the main missing features at the time of this writing are:
* videos
* reporting posts to admins
* muting conversations
* backfill of posts
* web-based signup
* profile metadata fields
* lists of users
* pinning posts to your profile
* polls
* scheduling posts
* account migration
* federated hashtag search
* shared block lists across servers

View file

@ -0,0 +1,45 @@
# Conversation Threads
Due to the nature of decentralization and federation, it is practically impossible for any one server on the fediverse to be aware of every post in a given conversation thread.
With that said, it is possible to do 'best effort' dereferencing of threads, whereby remote replies are fetched from one server onto another, to try to more fully flesh out a conversation.
GoToSocial does this by iterating up and down the thread of a conversation, pulling in remote statuses where possible.
## Example
Let's say we have two accounts: `local_account` on `our.server`, and `remote_1` on `remote.1`.
In this scenario, `local_account` follows `remote_1`, so posts from `remote_1` show up in the home timeline of `local_account`.
Now, `remote_1` boosts/reblogs a post from a third account, `remote_2`, residing on server `remote.2`.
`local_account` does not follow `remote_2`, and neither does anybody else on `our.server`, which means that `our.server` has not seen this post by `remote_2` before.
![A diagram of the conversation thread, showing the post from remote_2, and possible ancestor and descendant posts](../../assets/diagrams/conversation_thread.png)
What GoToSocial will do now, is 'dereference' the post by `remote_2` to check if it is part of a thread and, if so, whether any other parts of the thread can be obtained.
GtS begins by checking the `inReplyTo` property of the post, which is set when a post is a reply to another post. [See here](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-inreplyto). If `inReplyTo` is set, GoToSocial derefences the replied-to post. If *this* post also has an `inReplyTo` set, then GoToSocial dereferences that too, and so on.
Once all of these **ancestors** of a status have been retrieved, GtS will begin working down through the **descendants** of posts.
It does this by checking the `replies` property of a derefenced post, and working through replies, and replies of replies. [See here](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-replies).
This process of thread dereferencing will likely involve making multiple HTTP calls to different servers, especially if the thread is long and complicated.
The end result of this dereferencing is that, assuming the reblogged post by `remote_2` was part of a thread, then `local_account` should now be able to see posts in the thread when they open the status on their home timeline. In other words, they will see replies from accounts on other servers (who they may not have come across yet), in addition to any previous and next posts in the thread as posted by `remote_2`.
This gives `local_account` a more complete view on the conversation, as opposed to just seeing the reblogged post in isolation and out of context. It also gives `local_account` the opportunity to discover new accounts to follow, based on replies to `remote_2`.
## Privacy and Security
During the dereferencing process, GoToSocial signs outgoing requests using the key of the actor who received the activity that necessitated dereferencing. To use the above example, this means that all dereferencing requests would be signed by `local_account`. This gives remote servers the ability to refuse these dereferencing requests, assuming that `local_account` is blocked by one or more participants in the conversation.
From GoToSocial's side, domain blocks will be respected during the dereferencing process, to avoid making calls to servers that `our.server` has blocked.
Individual account blocks will also be respected, meaning that `our.server` won't try to dereference posts from accounts blocked by `local_account`.
Finally, GoToSocial expects that remote servers will only list replies that are marked as public (either `to` or `cc`). GtS may *try* to dereference followers-only posts, but it will assume that remote servers will check whether or not `local_account` is allowed to view them, and refuse accordingly.
Of course, when `local_account` opens up the conversation thread in whatever application they are using, GoToSocial will apply the usual post visibility filtering to ensure that they do not see any posts that they shouldn't have access to.

View file

@ -0,0 +1,49 @@
# ActivityPub Outbox
GoToSocial implements Outboxes for Actors (ie., instance accounts) following the ActivityPub specification [here](https://www.w3.org/TR/activitypub/#outbox).
To get an [OrderedCollection](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-orderedcollection) of Activities that an Actor has published recently, remote servers can do a `GET` request to a user's outbox. The address of this will be something like `https://example.org/users/whatever/outbox`.
The server will return an OrderedCollection of the following structure:
```json
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://example.org/users/whatever/outbox",
"type": "OrderedCollection",
"first": "https://example.org/users/whatever/outbox?page=true"
}
```
Note that the `OrderedCollection` itself contains no items. Callers must dereference the `first` page to start getting items. For example, a `GET` to `https://example.org/users/whatever/outbox?page=true` will produce something like the following:
```json
{
"id": "https://example.org/users/whatever/outbox?page=true",
"type": "OrderedCollectionPage",
"next": "https://example.org/users/whatever/outbox?max_id=01FJC1Q0E3SSQR59TD2M1KP4V8&page=true",
"prev": "https://example.org/users/whatever/outbox?min_id=01FJC1Q0E3SSQR59TD2M1KP4V8&page=true",
"partOf": "https://example.org/users/whatever/outbox",
"orderedItems": [
{
"id": "https://example.org/users/whatever/statuses/01FJC1MKPVX2VMWP2ST93Q90K7/activity",
"type": "Create",
"actor": "https://example.org/users/whatever",
"published": "2021-10-18T20:06:18Z",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [
"https://example.org/users/whatever/followers"
],
"object": "https://example.org/users/whatever/statuses/01FJC1MKPVX2VMWP2ST93Q90K7"
}
]
}
```
The `orderedItems` array will contain up to 30 entries. To get more entries beyond that, the caller can use the `next` link provided in the response.
Note that in the returned `orderedItems`, all activity types will be `Create`. On each activity, the `object` field will be the AP URI of an original public status created by the Actor who owns the Outbox (ie., a `Note` with `https://www.w3.org/ns/activitystreams#Public` in the `to` field, which is not a reply to another status). Callers can use the returned AP URIs to dereference the content of the notes.
Contrary to the ActivityPub spec, GoToSocial will deny requests that are not HTTP signed--that is, unauthenticated requests. This is consistent with GoToSocial's authentication policies for other federation API endpoints. This is to ensure that GoToSocial can deny requests from domains or users that have been blocked either by the GoToSocial instance itself (domain block), or by the individual owner of the Outbox.

View file

@ -0,0 +1,3 @@
# Request Throttling and Rate Limiting
GoToSocial applies rate limiting and http request throttling to the ActivityPub API endpoints (inboxes, user endpoints, emojis, etc). For more details on this, please see the [throttling](../../api/throttling.md) and [rate limiting](../../api/ratelimiting.md) documents.

View file

@ -1,429 +0,0 @@
# Federating with GoToSocial
Information on the various (ActivityPub) elements needed to federate with GoToSocial.
## HTTP Signatures
GoToSocial requires all `GET` and `POST` requests to ActivityPub s2s endpoints to be accompanied by a valid http signature.
GoToSocial will also sign all outgoing `GET` and `POST` requests that it makes to other servers.
This behavior is the equivalent of Mastodon's [AUTHORIZED_FETCH / "secure mode"](https://docs.joinmastodon.org/admin/config/#authorized_fetch).
GoToSocial uses the [go-fed/httpsig](https://github.com/go-fed/httpsig) library for signing outgoing requests, and for parsing and validating the signatures of incoming requests. This library strictly follows the [Cavage http signature RFC](https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures), which is the same RFC used by other implementations like Mastodon, Pixelfed, Akkoma/Pleroma, etc. (This RFC has since been superceded by the [httpbis http signature RFC](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures), but this is not yet widely implemented.)
### Incoming Requests
GoToSocial request signature validation is implemented in [internal/federation](https://github.com/superseriousbusiness/gotosocial/blob/main/internal/federation/authenticate.go).
GoToSocial will attempt to parse the signature using the following algorithms (in order), stopping at the first success:
```text
RSA_SHA256
RSA_SHA512
ED25519
```
### Outgoing Requests
GoToSocial request signing is implemented in [internal/transport](https://github.com/superseriousbusiness/gotosocial/blob/main/internal/transport/signing.go).
When assembling signatures:
- outgoing `GET` requests use `(request-target) host date`
- outgoing `POST` requests use `(request-target) host date digest`
GoToSocial uses the `RSA_SHA256` algorithm for signing requests, which is in line with other ActivityPub implementations.
### Quirks
The `keyId` used by GoToSocial in the `Signature` header will look something like the following:
```text
https://example.org/users/example_user/main-key
```
This is different from most other implementations, which usually use a fragment (`#`) in the `keyId` uri. For example, on Mastodon the user's key would instead be found at:
```text
https://example.org/users/example_user#main-key
```
For Mastodon, the public key of a user is served as part of that user's Actor representation. GoToSocial mimics this behavior when serving the public key of a user, but instead of returning the entire Actor at the `main-key` endpoint (which may contain sensitive fields), will return only a partial stub of the actor. This looks like the following:
```json
{
"@context": [
"https://w3id.org/security/v1",
"https://www.w3.org/ns/activitystreams"
],
"id": "https://example.org/users/example_user",
"preferredUsername": "example_user",
"publicKey": {
"id": "https://example.org/users/example_user/main-key",
"owner": "https://example.org/users/example_user",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzGB3yDvMl+8p+ViutVRG\nVDl9FO7ZURYXnwB3TedSfG13jyskoiMDNvsbLoUQM9ajZPB0zxJPZUlB/W3BWHRC\nNFQglE5DkB30GjTClNZoOrx64vLRT5wAEwIOjklKVNk9GJi1hFFxrgj931WtxyML\nBvo+TdEblBcoru6MKAov8IU4JjQj5KUmjnW12Rox8dj/rfGtdaH8uJ14vLgvlrAb\neQbN5Ghaxh9DGTo1337O9a9qOsir8YQqazl8ahzS2gvYleV+ou09RDhS75q9hdF2\nLI+1IvFEQ2ZO2tLk3umUP1ioa+5CWKsWD0GAXbQu9uunAV0VoExP4+/9WYOuP0ei\nKwIDAQAB\n-----END PUBLIC KEY-----\n"
},
"type": "Person"
}
```
Remote servers federating with GoToSocial should extract the public key from the `publicKey` field. Then, they should use the `owner` field of the public key to further dereference the full version of the Actor, using a signed `GET` request.
This behavior was introduced as a way of avoiding having remote servers make unsigned `GET` requests to the full Actor endpoint. However, this may change in future as it is not compliant and causes issues. Tracked in [this issue](https://github.com/superseriousbusiness/gotosocial/issues/1186).
## Access Control
GoToSocial uses access control restrictions to protect users and resources from unwanted interactions with remote accounts and instances.
As shown in the [HTTP Signatures](#http-signatures) section, GoToSocial requires all incoming `GET` and `POST` requests from remote servers to be signed. Unsigned requests will be denied with http code `401 Unauthorized`.
Access control restrictions are implemented by checking the `keyId` of the signature (who owns the public/private key pair making the request).
First, the host value of the `keyId` uri is checked against the GoToSocial instance's list of blocked (defederated) domains. If the host is recognized as a blocked domain, then the http request will immediately be aborted with http code `403 Forbidden`.
Next, GoToSocial will check for the existence of a block (in either direction) between the owner of the public key making the http request, and the owner of the resource that the request is targeting. If the GoToSocial user blocks the remote account making the request, then the request will be aborted with http code `403 Forbidden`.
## Request Throttling & Rate Limiting
GoToSocial applies http request throttling and rate limiting to the ActivityPub API endpoints (inboxes, user endpoints, emojis, etc).
This ensures that remote servers cannot flood a GoToSocial instance with spurious requests. Instead, remote servers making GET or POST requests to the ActivityPub API endpoints should respect 429 and 503 http codes, and take account of the `retry-after` http response header.
For more details on request throttling and rate limiting behavior, please see the [throttling](../api/throttling.md) and [rate limiting](../api/ratelimiting.md) documents.
## Outbox
GoToSocial implements Outboxes for Actors (ie., instance accounts) following the ActivityPub specification [here](https://www.w3.org/TR/activitypub/#outbox).
To get an [OrderedCollection](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-orderedcollection) of Activities that an Actor has published recently, remote servers can do a `GET` request to a user's outbox. The address of this will be something like `https://example.org/users/whatever/outbox`.
The server will return an OrderedCollection of the following structure:
```json
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://example.org/users/whatever/outbox",
"type": "OrderedCollection",
"first": "https://example.org/users/whatever/outbox?page=true"
}
```
Note that the `OrderedCollection` itself contains no items. Callers must dereference the `first` page to start getting items. For example, a `GET` to `https://example.org/users/whatever/outbox?page=true` will produce something like the following:
```json
{
"id": "https://example.org/users/whatever/outbox?page=true",
"type": "OrderedCollectionPage",
"next": "https://example.org/users/whatever/outbox?max_id=01FJC1Q0E3SSQR59TD2M1KP4V8&page=true",
"prev": "https://example.org/users/whatever/outbox?min_id=01FJC1Q0E3SSQR59TD2M1KP4V8&page=true",
"partOf": "https://example.org/users/whatever/outbox",
"orderedItems": [
{
"id": "https://example.org/users/whatever/statuses/01FJC1MKPVX2VMWP2ST93Q90K7/activity",
"type": "Create",
"actor": "https://example.org/users/whatever",
"published": "2021-10-18T20:06:18Z",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [
"https://example.org/users/whatever/followers"
],
"object": "https://example.org/users/whatever/statuses/01FJC1MKPVX2VMWP2ST93Q90K7"
}
]
}
```
The `orderedItems` array will contain up to 30 entries. To get more entries beyond that, the caller can use the `next` link provided in the response.
Note that in the returned `orderedItems`, all activity types will be `Create`. On each activity, the `object` field will be the AP URI of an original public status created by the Actor who owns the Outbox (ie., a `Note` with `https://www.w3.org/ns/activitystreams#Public` in the `to` field, which is not a reply to another status). Callers can use the returned AP URIs to dereference the content of the notes.
## Conversation Threads
Due to the nature of decentralization and federation, it is practically impossible for any one server on the fediverse to be aware of every post in a given conversation thread.
With that said, it is possible to do 'best effort' dereferencing of threads, whereby remote replies are fetched from one server onto another, to try to more fully flesh out a conversation.
GoToSocial does this by iterating up and down the thread of a conversation, pulling in remote statuses where possible.
Let's say we have two accounts: `local_account` on `our.server`, and `remote_1` on `remote.1`.
In this scenario, `local_account` follows `remote_1`, so posts from `remote_1` show up in the home timeline of `local_account`.
Now, `remote_1` boosts/reblogs a post from a third account, `remote_2`, residing on server `remote.2`.
`local_account` does not follow `remote_2`, and neither does anybody else on `our.server`, which means that `our.server` has not seen this post by `remote_2` before.
![A diagram of the conversation thread, showing the post from remote_2, and possible ancestor and descendant posts](../assets/diagrams/conversation_thread.png)
What GoToSocial will do now, is 'dereference' the post by `remote_2` to check if it is part of a thread and, if so, whether any other parts of the thread can be obtained.
GtS begins by checking the `inReplyTo` property of the post, which is set when a post is a reply to another post. [See here](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-inreplyto). If `inReplyTo` is set, GoToSocial derefences the replied-to post. If *this* post also has an `inReplyTo` set, then GoToSocial dereferences that too, and so on.
Once all of these **ancestors** of a status have been retrieved, GtS will begin working down through the **descendants** of posts.
It does this by checking the `replies` property of a derefenced post, and working through replies, and replies of replies. [See here](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-replies).
This process of thread dereferencing will likely involve making multiple HTTP calls to different servers, especially if the thread is long and complicated.
The end result of this dereferencing is that, assuming the reblogged post by `remote_2` was part of a thread, then `local_account` should now be able to see posts in the thread when they open the status on their home timeline. In other words, they will see replies from accounts on other servers (who they may not have come across yet), in addition to any previous and next posts in the thread as posted by `remote_2`.
This gives `local_account` a more complete view on the conversation, as opposed to just seeing the reblogged post in isolation and out of context. It also gives `local_account` the opportunity to discover new accounts to follow, based on replies to `remote_2`.
## Reports / Flags
Like other microblogging ActivityPub implementations, GoToSocial uses the [Flag](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-flag) Activity type to communicate user moderation reports to other servers.
### Outgoing
The json of an outgoing GoToSocial `Flag` looks like the following:
```json
{
"@context": "https://www.w3.org/ns/activitystreams",
"actor": "http://example.org/users/example.org",
"content": "dark souls sucks, please yeet this nerd",
"id": "http://example.org/reports/01GP3AWY4CRDVRNZKW0TEAMB5R",
"object": [
"http://fossbros-anonymous.io/users/foss_satan",
"http://fossbros-anonymous.io/users/foss_satan/statuses/01FVW7JHQFSFK166WWKR8CBA6M"
],
"type": "Flag"
}
```
The `actor` of the `Flag` will always be the instance actor of the GoToSocial instance on which the `Flag` was created. This is done to preserve partial anonymity of the user who created the report, in order to prevent them becoming a target for harassment.
The `content` of the `Flag` is a piece of text submitted by the user who created the `Flag`, which should give remote instance admins a reason why the report was created. This may be an empty string, or may not be present on the json, if no reason was submitted by the user.
The value of the `object` field of the `Flag` will either be a string (the ActivityPub `id` of the user being reported), or it will be an array of strings, where the first entry in the array is the `id` of the reported user, and subsequent entries are the `id`s of one or more reported `Note`s / statuses.
The `Flag` activity is delivered as-is to the `inbox` (or shared inbox) of the reported user. It is not wrapped in a `Create` activity.
### Incoming
GoToSocial assumes incoming reports will be delivered as a `Flag` Activity to the `inbox` of the account being reported. It will parse the incoming `Flag` following the same formula that it uses for creating outgoing `Flag`s, with one difference: it will attempt to parse status URLs from both the `object` field, and from a Misskey/Calckey-formatted `content` value, which includes in-line status URLs.
GoToSocial will not assume that the `to` field will be set on an incoming `Flag` activity. Instead, it assumes that remote instances use `bto` to direct the `Flag` to its recipient.
A valid incoming `Flag` Activity will be made available as a report to the admin(s) of the GoToSocial instance that received the report, so that they can take any necessary moderation action against the reported user.
The reported user themself will not see the report, or be notified that they have been reported, unless the GtS admin chooses to share this information with them via some other channel.
## Featured (aka pinned) Posts
GoToSocial allows users to feature (or 'pin') posts on their profile.
In ActivityPub terms, GoToSocial serves these pinned posts as an [OrderedCollection](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-orderedcollection) at the endpoint indicated in an Actor's [featured](https://docs.joinmastodon.org/spec/activitypub/#featured) field. The value of this field will be set to something like `https://example.org/users/some_user/collections/featured`.
By making a signed GET request to this endpoint, remote instances can dereference the featured posts collection, which will return an `OrderedCollection` with a list of post URIs in the `orderedItems` field.
Example of a featured collection of a user who has pinned multiple `Note`s:
```json
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://example.org/users/some_user/collections/featured",
"orderedItems": [
"https://example.org/users/some_user/statuses/01GS7VTYH0S77NNXTP6W4G9EAG",
"https://example.org/users/some_user/statuses/01GSFY2SZK9TPCJFQ1WCCPGDRT",
"https://example.org/users/some_user/statuses/01GSCXY70MZCBFMH5EKJW9ENC8"
],
"totalItems": 3,
"type": "OrderedCollection"
}
```
Example of a user who has pinned one `Note`:
```json
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://example.org/users/some_user/collections/featured",
"orderedItems": [
"https://example.org/users/some_user/statuses/01GS7VTYH0S77NNXTP6W4G9EAG"
],
"totalItems": 1,
"type": "OrderedCollection"
}
```
Example with no pinned `Note`s:
```json
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://example.org/users/some_user/collections/featured",
"orderedItems": [],
"totalItems": 0,
"type": "OrderedCollection"
}
```
Unlike Mastodon and some other implementations, GoToSocial does *not* serve full `Note` representations as `orderedItems` values. Instead, it provides just the URI of each `Note`, which the remote server can then dereference (or not, if they already have the `Note` cached locally).
Some of the URIs served as part of the collection may point to followers-only posts which the requesting `Actor` won't necessarily have permission to view. Remote servers should make sure to do their own filtering (as with any other post type) to ensure that these posts are only shown to users who are permitted to view them.
Another difference between GoToSocial and other server implementations is that GoToSocial does not send updates to remote servers when a post is pinned or unpinned by a user. Mastodon does this by sending [Add](https://www.w3.org/TR/activitypub/#add-activity-inbox) and [Remove](https://www.w3.org/TR/activitypub/#remove-activity-inbox) Activity types where the `object` is the post being pinned or unpinned, and the `target` is the sending `Actor`'s `featured` collection. While this conceptually makes sense, it is not in line with what the ActivityPub protocol recommends, since the `target` of the Activity "is not owned by the receiving server, and thus they can't update it".
Instead, to build a view of a GoToSocial user's pinned posts, it is recommended that remote instances simply poll a GoToSocial Actor's `featured` collection every so often, and add/remove posts in their cached representation as appropriate.
## Post Deletes
GoToSocial allows users to delete posts that they have created. These deletes will be federated out to other instances, which are expected to also delete their local cache of the post.
### Outgoing
When a post is deleted by a GoToSocial user, the server will send a `Delete` activity out to other instances.
The `Delete` will have the ActivityPub URI of the post set as the value of the `Object` entry.
`to` and `cc` will be set according to the visibility of the original post, and any users mentioned/replied to by the original post.
If the original post was not a direct message, the ActivityPub `Public` URI will be addressed in `to`. Otherwise, only mentioned and replied to users will be addressed.
In the following example, the 'admin' user deletes a public post of theirs in which the 'foss_satan' user was mentioned:
```json
{
"@context": "https://www.w3.org/ns/activitystreams",
"actor": "http://example.org/users/admin",
"cc": [
"http://example.org/users/admin/followers",
"http://fossbros-anonymous.io/users/foss_satan"
],
"object": "http://example.org/users/admin/statuses/01FF25D5Q0DH7CHD57CTRS6WK0",
"to": "https://www.w3.org/ns/activitystreams#Public",
"type": "Delete"
}
```
In the next example, the '1happyturtle' user deletes a direct message which was originally addressed to the 'the_mighty_zork' user.
```json
{
"@context": "https://www.w3.org/ns/activitystreams",
"actor": "http://example.org/users/1happyturtle",
"cc": [],
"object": "http://example.org/users/1happyturtle/statuses/01FN3VJGFH10KR7S2PB0GFJZYG",
"to": "http://somewhere.com/users/the_mighty_zork",
"type": "Delete"
}
```
To process a `Delete` activity coming from a GoToSocial instance, remote instances should check if they have the `Object` stored according to the provided URI. If they do, they should remove it from their local cache. If not, then no action is required, since they never had the now-deleted post stored in the first place.
### Incoming
GoToSocial processes `Delete` activities coming in from remote instances as thoroughly as possible in order to respect the privacy of other users.
When a GoToSocial instance receives a `Delete`, it will attempt to derive the deleted post URI from the `Object` field. If the `Object` is just a URI, then this URI will be taken. If the `Object` is a `Note` or another type commonly used to represent a post, then the URI will be extracted from it.
Then, GoToSocial will check if it has a post stored with the given URI. If it does, it will be completely deleted from the database and all user timelines.
GoToSocial will only delete a post if it can be sure that the original post was owned by the `actor` that the `Delete` is attributed to.
## Profile Fields
Like Mastodon and other fediverse softwares, GoToSocial lets users set key/value pairs on their profile; useful for conveying short pieces of information like links, pronouns, age, etc.
For the sake of compatibility with other implementations, GoToSocial uses the same schema.org PropertyValue extension that Mastodon uses, present as an `attachment` array value on `actor`s that have fields set. For example, the below JSON shows an account with two PropertyValue fields:
```json
{
"@context": [
"http://joinmastodon.org/ns",
"https://w3id.org/security/v1",
"https://www.w3.org/ns/activitystreams",
"http://schema.org"
],
"attachment": [
{
"name": "should you follow me?",
"type": "PropertyValue",
"value": "maybe!"
},
{
"name": "age",
"type": "PropertyValue",
"value": "120"
}
],
"discoverable": false,
"featured": "http://example.org/users/1happyturtle/collections/featured",
"followers": "http://example.org/users/1happyturtle/followers",
"following": "http://example.org/users/1happyturtle/following",
"id": "http://example.org/users/1happyturtle",
"inbox": "http://example.org/users/1happyturtle/inbox",
"manuallyApprovesFollowers": true,
"name": "happy little turtle :3",
"outbox": "http://example.org/users/1happyturtle/outbox",
"preferredUsername": "1happyturtle",
"publicKey": {
"id": "http://example.org/users/1happyturtle#main-key",
"owner": "http://example.org/users/1happyturtle",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtTc6Jpg6LrRPhVQG4KLz\n2+YqEUUtZPd4YR+TKXuCnwEG9ZNGhgP046xa9h3EWzrZXaOhXvkUQgJuRqPrAcfN\nvc8jBHV2xrUeD8pu/MWKEabAsA/tgCv3nUC47HQ3/c12aHfYoPz3ufWsGGnrkhci\nv8PaveJ3LohO5vjCn1yZ00v6osMJMViEZvZQaazyE9A8FwraIexXabDpoy7tkHRg\nA1fvSkg4FeSG1XMcIz2NN7xyUuFACD+XkuOk7UqzRd4cjPUPLxiDwIsTlcgGOd3E\nUFMWVlPxSGjY2hIKa3lEHytaYK9IMYdSuyCsJshd3/yYC9LqxZY2KdlKJ80VOVyh\nyQIDAQAB\n-----END PUBLIC KEY-----\n"
},
"summary": "\u003cp\u003ei post about things that concern me\u003c/p\u003e",
"tag": [],
"type": "Person",
"url": "http://example.org/@1happyturtle"
}
```
For `actor`s that have no `PropertyValue` fields set, the `attachment` property will not be set at all. That is, the `attachment` key value will not be present on the `actor` (not even as an empty array or null value).
While `attachment` is not technically an ordered collection, GoToSocial--again, in line with what other implementations do--does present `attachment` `PropertyValue` fields in the order in which they should to be displayed.
GoToSocial will also parse PropertyValue fields from remote `actor`s discovered by the GoToSocial instance, to allow them to be displayed to users on the GoToSocial instance.
GoToSocial allows up to 6 `PropertyValue` fields by default, as opposed to Mastodon's default 4.
## Hashtags
GoToSocial users can include hashtags in their posts, which indicate to other instances that that user wishes their post to be grouped together with other posts using the same hashtag, for discovery purposes.
In line with other ActivityPub server implementations, GoToSocial implicitly expects that only public-addressed posts will be grouped by hashtag.
To federate hashtags in and out, GoToSocial uses the widely-adopted [ActivityStreams `Hashtag` type extension](https://www.w3.org/wiki/Activity_Streams_extensions#as:Hashtag_type) in the `tag` property of objects.
Here's what the `tag` property might look like on an outgoing message that uses one custom emoji, and one tag:
```json
"tag": [
{
"icon": {
"mediaType": "image/png",
"type": "Image",
"url": "https://example.org/fileserver/01AY6P665V14JJR0AFVRT7311Y/emoji/original/01F8MH9H8E4VG3KDYJR9EGPXCQ.png"
},
"id": "https://example.org/emoji/01F8MH9H8E4VG3KDYJR9EGPXCQ",
"name": ":rainbow:",
"type": "Emoji",
"updated": "2021-09-20T10:40:37Z"
},
{
"href": "https://example.org/tags/welcome",
"name": "#welcome",
"type": "Hashtag"
}
]
```
With just one tag, the `tag` property will be an object rather than an array, which will look like this:
```json
"tag": {
"href": "https://example.org/tags/welcome",
"name": "#welcome",
"type": "Hashtag"
}
```
### Hashtag `href` property
The `href` URL provided by GoToSocial in outgoing tags points to a web URL that serves `text/html`.
GoToSocial makes no guarantees whatsoever about what the content of the given `text/html` will be, and remote servers should not interpret the URL as a canonical ActivityPub ID/URI property. The `href` URL is provided merely as an endpoint which *might* contain more information about the given hashtag.

View file

@ -0,0 +1,7 @@
# Security
TODO: describe the security model we use for federation.
* http signatures
* behavior for refusing requests
* how data is protected

View file

@ -1,75 +0,0 @@
# Deployment considerations
Before deploying GoToSocial, it's important to think through a few things as some choices will have long-term consequences for how you run and manage GoToSocial.
!!! danger
It's not supported across the Fediverse to switch between implementations on the same domain. This means that if you run GoToSocial on example.org, you'll run into federation issues if you try to switch to a different implementation like Pleroma/Akkoma, Misskey/Calckey etc.
In that same vein, if you already have another ActivityPub implementation running on example.org you should not attempt to switch to GoToSocial on that domain.
## Database
GoToSocial supports both SQLite and Postgres and you can start using either. We do not currently have tooling to support migrating from SQLite to Postgres or vice-versa, but it is possible in theory.
SQLite is great for a single-user instance. If you're planning on hosting multiple people it's advisable to use Postgres instead. You can always use Postgres regardless of the instance size.
!!! tip
Please [backup your database](../admin/backup_and_restore.md). The database contains encryption keys for the instance and any user accounts. You won't be able to federate again from the same domain if you lose these keys.
## Domain name
In order to federate with others, you'll need a domain like `example.org`. You can register your domain name through any domain registrar, like [Namecheap](https://www.namecheap.com/). Make sure you pick a registrar that also lets you manage DNS entries, so you can point your domain to the IP of the server that's running your GoToSocial instance.
You'll commonly see usernames existing at the apex of the domain, for example `@me@example.org` but this is not required. It's perfectly fine to have users exist on `@me@social.example.org` instead. Many people prefer to have usernames on the apex as its shorter to type, but you can use any (subdomain) you control.
It is possible to have usernames like `@me@example.org` but have GoToSocial running on `social.example.org` instead. This is done by distinguishing between the API domain, called the "host", and the domain used for usernames, called the "account domain".
!!! danger
It's not possible to safely change whether the host and account domain are different after the fact. It requires regenerating the database and will cause confusion for any server you have already federated with.
When using a single domain, you only need to configure the "host" in the GoToSocial configuration:
```yaml
host: "example.org"
```
When using a split domain approach, you need to configure both the "host" and the "account-domain":
```yaml
host: "social.example.org"
account-domain: "example.org"
```
## TLS
For federation to work, you have to use TLS. Most implementations, including GoToSocial, will generally refuse to federate over unencrypted transports.
GoToSocial comes with built-in support for provisioning certificates through Lets Encrypt. It can also load certificates from disk. If you have a reverse-proxy in front of GoToSocial you can handle TLS at that level instead.
!!! tip
Make sure you configure the use of modern versions of TLS, TLSv1.2 and higher, in order to keep communications between servers and clients safe. When GoToSocial handles TLS termination this is done automatically for you. If you have a reverse-proxy in use, use the [Mozilla SSL Configuration Generator](https://ssl-config.mozilla.org/).
## Server / VPS
GoToSocial aims to fit in small spaces so we try and ensure that the system requirements are fairly minimal: for a single-user instance with about 100 followers/followees, it uses somewhere between 50 to 100MB of RAM. CPU usage is only intensive when handling media (encoding blurhashes, mostly) and/or doing a lot of federation requests at the same time.
These light requirements mean GtS runs pretty well on something like a Raspberry Pi (a €40 single-board computer). It's been tested on a Raspberry Pi Zero W as well (a €9 computer smaller than a credit card), but it's not quite able to run on that. It should run on a Raspberry Pi Zero W 2 (which costs €14!), but we haven't tested that yet. You can also repurpose an old laptop or desktop to run GoToSocial for you.
If you decide to use a VPS instead, you can spin yourself up something cheap with Linux running on it. Most of the VPS offerings in the €2-€5 range will perform admirably for a personal GoToSocial instance.
[Hostwinds](https://www.hostwinds.com/) is a good option here: it's cheap and they throw in a static IP address for free.
[Greenhost](https://greenhost.net) is also great: it has zero CO2 emissions, but is a bit more costly.
## Ports
GoToSocial needs ports `80` and `443` open.
* `80` is used for Lets Encrypt. As such, you don't need it if you don't use the built-in Lets Encrypt provisioning.
* `443` is used to serve the API on with TLS and is what any instance you're federating with will try to connect to.
If you can't leave `443` and `80` open on the machine, don't worry! You can configure these ports in GoToSocial, but you'll have to also configure port forwarding to properly forward traffic on `443` and `80` to whatever ports you choose.
!!! tip
You should configure a firewall on your machine, as well as some protection against brute-force SSH login attempts and the like. Take a look at our [firewall documentation](../advanced/security/firewall.md) for pointers on what to configure and tools that can help you out.

View file

@ -1,123 +0,0 @@
# Container
This guide walks you through getting GoToSocial up and running using the official container images we publish. In this case we'll be using the Docker runtime directly through [Docker Compose](https://docs.docker.com/compose) together with SQLite as the database.
You can also run GoToSocial using a container orchestration system such as [Kubernetes](https://kubernetes.io/) or [Nomad](https://www.nomadproject.io/), but that is beyond the scope of this guide.
## Create a Working Directory
You need a working directory in which your docker-compose file will be located, and a directory for GoToSocial to store data in, so create these directories with the following command:
```bash
mkdir -p ~/gotosocial/data
```
Now change to the working directory you created:
```bash
cd ~/gotosocial
```
## Get the latest docker-compose.yaml
Use `wget` to download the latest [docker-compose.yaml](https://raw.githubusercontent.com/superseriousbusiness/gotosocial/main/example/docker-compose/docker-compose.yaml) example, which we'll customize for our needs:
```bash
wget https://raw.githubusercontent.com/superseriousbusiness/gotosocial/main/example/docker-compose/docker-compose.yaml
```
## Edit the docker-compose.yaml
Because GoToSocial can be configured using [Environment Variables](../../configuration/index.md#environment-variables), we can skip mounting a config.yaml file into the container, to make our configuration simpler. We just need to edit the docker-compose.yaml file to change a few things.
First open the docker-compose.yaml file in your editor of choice. For example:
```bash
nano docker-compose.yaml
```
### Version
If desired, update the GoToSocial Docker image tag to the version of GtS you want to use.
`latest` - the default. This points to the latest stable release of GoToSocial.
`snapshot` - points to whatever code is currently on the main branch. Not guaranteed to be stable, and may often be broken. Use with caution.
!!! tip
You can also replace `latest` with a specific GoToSocial version number. This is recommended when you want to make sure that you don't update your GoToSocial version by accident, which can cause problems. The list of releases can be found [right here](https://github.com/superseriousbusiness/gotosocial/releases), with the newest release at the top. Replace `latest` in the docker-compose.yaml with the number of the desired release (without the leading `v` or trailing version name).
### Host
Change the `GTS_HOST` environment variable to the domain you are running GoToSocial on.
### Server Timezone (optional but recommended)
To ensure that your GoToSocial server displays the correct time on posts and in logs, you can set the timezone of the server by editing the `TZ` environment variable.
1. Remove the `#` before `TZ: UTC` in the environment section.
2. Change the `UTC` part to your timezone identifier. For a list of these identifiers, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones.
For example, if you are running your server in Minsk, you would set `TZ: Europe/Minsk`, Japan would be `TZ: Japan`, Dubai would be `TZ: Asia/Dubai`, etc.
If you don't set this, the default `UTC` will be used.
### User (optional / probably not necessary)
By default, Dockerized GoToSocial runs with Linux user/group `1000:1000`, which is fine in most cases. If you want to run as a different user/group, you should change the `user` field in the docker-compose.yaml accordingly.
For example, let's say you created the `~/gotosocial/data` directory for a user with id `1001`, and group id `1001`. If you now try to run GoToSocial without changing the `user` field, it will get a permissions error trying to open its database file in the directory. In this case, you would have to change the `user` field of the docker compose file to `1001:1001`.
### LetsEncrypt (optional)
If you want to use [LetsEncrypt](../../configuration/tls.md) for TLS certificates (https), you should also:
1. Change the value of `GTS_LETSENCRYPT_ENABLED` to `"true"`.
2. Remove the `#` before `- "80:80"` in the `ports` section.
3. (Optional) Set `GTS_LETSENCRYPT_EMAIL_ADDRESS` to a valid email address to receive certificate expiry warnings etc.
## Start GoToSocial
With those small changes out of the way, you can now start GoToSocial with the following command:
```shell
docker-compose up -d
```
After running this command, you should get an output like:
```text
Creating network "gotosocial_gotosocial" with the default driver
Creating gotosocial ... done
```
If you want to follow the logs of GoToSocial, you can use:
```bash
docker logs -f gotosocial
```
If everything is OK, you should see something similar to the following:
```text
time=2022-04-19T09:48:35Z level=info msg=connected to SQLITE database
time=2022-04-19T09:48:35Z level=info msg=MIGRATED DATABASE TO group #1 (20211113114307, 20220214175650, 20220305130328, 20220315160814) func=doMigration
time=2022-04-19T09:48:36Z level=info msg=instance account example.org CREATED with id 01EXX0TJ9PPPXF2C4N2MMMVK50
time=2022-04-19T09:48:36Z level=info msg=created instance instance example.org with id 01PQT31C7BZJ1Q2Z4BMEV90ZCV
time=2022-04-19T09:48:36Z level=info msg=media manager cron logger: start[]
time=2022-04-19T09:48:36Z level=info msg=media manager cron logger: schedule[now 2022-04-19 09:48:36.096127852 +0000 UTC entry 1 next 2022-04-20 00:00:00 +0000 UTC]
time=2022-04-19T09:48:36Z level=info msg=started media manager remote cache cleanup job: will run next at 2022-04-20 00:00:00 +0000 UTC
time=2022-04-19T09:48:36Z level=info msg=listening on 0.0.0.0:8080
```
## Create your first User
Now that GoToSocial is running, you should create at least a user for yourself. How to do so is documented in our [Creating users](../user_creation.md) guide.
### Done
GoToSocial should now be running on your machine! To verify this, open your browser navigate to whatever you set as your `GTS_HOST` value. You should see the GoToSocial landing page. Well done!
## (Optional) Reverse Proxy
If you want to run other webservers on port 443 or want to add an additional layer of security you might want to use a [reverse proxy](../reverse_proxy/index.md). We have guides available for a couple of popular open source options and will gladly take pull requests to add more.

View file

@ -1,14 +0,0 @@
# Installation
As we noted in [Releases](../releases.md), we publish official binary release as well as containers. We have a number of guides available on how to deploy your own GoToSocial instance this way.
Before proceeding with your installation, please ensure you've read through the [Deployment considerations](../index.md) first and have a domain and server ready to go.
Also take a minute to familiarise yourself with [how to configure](../../configuration/index.md) GoToSocial.
## Guides
For third-party releases we don't provide guides on how to use them. You need to refer to their own documentation instead. Our guides might still be useful to review in order to familiarise yourself with which configuration options you likely want to set and tweak.
* [Bare metal](metal.md)
* [Container](container.md)

View file

@ -1,40 +0,0 @@
# Releases
GoToSocial can be installed in a number of different ways. We publish official binary releases as well as container images. A number of third-party packages are maintained by different distributions and some people have created additional deployment tooling to make it easy to deploy GoToSocial yourself.
## Binary releases
We publish binary builds for Linux to [our GitHub project](https://github.com/superseriousbusiness/gotosocial/releases):
* 32-bit Intel/AMD (i386/x86)
* 64-bit Intel/AMD (amd64/x86_64)
* 32-bit ARM (v6 and v7)
* 64-bit ARM64
For FreeBSD we publish:
* 64-bit Intel/AMD (amd64/x86_64)
## Containers
We also publish container images [on the Docker Hub](https://hub.docker.com/r/superseriousbusiness/gotosocial).
Containers are released for the same Linux platforms as our binary releases, with the exception of 32-bit Intel/AMD.
## Third-party
Some folks have created distribution packages for GoToSocial or additional tooling to aid in installing GoToSocial.
### Distribution packages
These packages are not maintained by GoToSocial, so please direct questions and issues to the repository maintainers (and donate to them!).
[![Packaging status](https://repology.org/badge/vertical-allrepos/gotosocial.svg)](https://repology.org/project/gotosocial/versions)
### Deployment tools
You can deploy your own instance of GoToSocial with the help of:
- [YunoHost GoToSocial Packaging](https://github.com/YunoHost-Apps/gotosocial_ynh) by [OniriCorpe](https://github.com/OniriCorpe).
- [Ansible Playbook (MASH)](https://github.com/mother-of-all-self-hosting/mash-playbook): The playbook supports a many services, including GoToSocial. [Documentation](https://github.com/mother-of-all-self-hosting/mash-playbook/blob/main/docs/services/gotosocial.md)
- GoToSocial Helm Charts:
- [GoToSocial Helm Chart](https://github.com/fSocietySocial/charts/tree/main/charts/gotosocial) by [0hlov3](https://github.com/0hlov3).

View file

@ -1,43 +0,0 @@
# Reverse proxy
GoToSocial can be exposed directly to the internet. However, many folks prefer to have a reverse proxy handle connections from the outside instead. This can also give greater control over TLS configurations and enables some more advanced scenario's like asset caching.
## General procedure
In order to use a reverse-proxy, you'll typically want to do a few things:
* Configure some way to get TLS certificates for the host domain
* Bind GoToSocial to a local IP instead of a public IP and a non-priviledged port. Adjust the `bind-address` and `port` configuration options
* Disable Lets Encrypt in GoToSocial if you were using it. Set `letsencrypt-enabled` to `false`
* Configure the reverse proxy to handle TLS and proxy requests to GoToSocial
!!! warning
Do not change the value of the `host` configuration option. This needs to remain the actual domain name the instance is running on as seen by other instances on the internet. Instead, change the `bind-address` and update the `port` and `trusted-proxies`.
### Container
When you deploy GoToSocial using our [example Docker Compose guide](../installation/container.md), it will bind to port `443` by default as it assumes you want to directly expose it to the internet. In order to run it behind a reverse proxy, you need to change that.
In the compose file:
* Comment out the `- "443:8080"` line in the `ports` definition
* If you had enabled Lets Encrypt support:
* Comment out the `- "80:80"` line in the `ports` definition
* Set `GTS_LETSENCRYPT_ENABLED` back to `"false"` or comment it out
* Uncomment the `- "127.0.0.1:8080:8080"` line instead
This now causes Docker to only forward connections on `127.0.0.1` on port `8080` to the container, effectively isolating it from the outside world. You can now tell your reverse-proxy to send requests there instead.
## Guides
We have guides available for the following servers:
* [nginx](nginx.md)
* [Apache httpd](apache-httpd.md)
* [Caddy 2](caddy.md)
## WebSockets
When using a reverse-proxy, special care must be taken to allow WebSockets to work too. This is necessary as many client applications use WebSockets to stream your timeline. WebSockets is not used as part of federation.
Make sure you read the [WebSocket](websocket.md) documentation and configure your reverse proxy accordingly.

View file

@ -1,50 +0,0 @@
# Creating users
Regardless of the installation method, you'll need to create some users. GoToSocial currently doesn't have a way for users to be created through the web UI, or for people to sign-up through the web UI.
Using the CLI, you can create a user:
```sh
$ gotosocial --config-path /path/to/config.yaml \
admin account create \
--username some_username \
--email some_email@whatever.org \
--password 'SOME_PASSWORD'
```
In the above command, replace `some_username` with your desired username, `some_email@whatever.org` with the email address you want to associate with your account, and `SOME_PASSWORD` with a secure password.
If you want your user to have admin rights, you can promote them using a similar command:
```sh
$ gotosocial --config-path /path/to/config.yaml \
admin account promote --username some_username
```
Replace `some_username` with the username of the account you just created.
!!! info
When running these commands, you'll get a bit of output like the following:
```text
time=XXXX level=info msg=connected to SQLITE database
time=XXXX level=info msg=there are no new migrations to run func=doMigration
time=XXXX level=info msg=closing db connection
```
This is normal and indicates that the commands ran as expected.
## Containers
When running GoToSocial from a container, you'll need to execute the above command in the conatiner instead. How to do this varies based on your container runtime, but for Docker it should look like:
```sh
$ docker exec -it CONTAINER_NAME_OR_ID \
/gotosocial/gotosocial \
admin account create \
--username some_username \
--email someone@example.org \
--password 'some_very_good_password'
```
If you followed our Docker guide, the container name will be `gotosocial`. Both the name and the ID can be retrieved through `docker ps`.

View file

@ -10,7 +10,7 @@ With GoToSocial, you can keep in touch with your friends, post, read, and share
GoToSocial provides a lightweight, customizable, and safety-focused entryway into the [Fediverse](https://en.wikipedia.org/wiki/Fediverse), and is comparable to (but distinct from) existing projects such as [Mastodon](https://joinmastodon.org/), [Pleroma](https://pleroma.social/), [Friendica](https://friendica.net), and [PixelFed](https://pixelfed.org/).
**GoToSocial is still [ALPHA SOFTWARE](https://en.wikipedia.org/wiki/Software_release_life_cycle#Alpha)**. It is already deployable and useable, and it federates cleanly with many other Fediverse servers (not yet all). However, many things are not yet implemented, and there are plenty of bugs! We foresee entering beta around the beginning of 2024.
**GoToSocial is still [ALPHA SOFTWARE](https://en.wikipedia.org/wiki/Software_release_life_cycle#Alpha)**. It is already deployable and useable, and it federates cleanly with many other Fediverse servers (not yet all). However, many things are not yet implemented, and there are plenty of bugs! We foresee entering beta somewhere in 2023.
Here's a screenshot of the instance landing page!
@ -38,7 +38,7 @@ Since GoToSocial is still in alpha, there are plenty of bugs. We use [GitHub iss
### Client App Issues
GoToSocial works great with Tusky and Semaphore, but some other client applications still need work or have issues connecting to GoToSocial. We're tracking them [right here](https://github.com/superseriousbusiness/gotosocial/projects/5). It's our goal to make any app that's compatible with the Mastodon API work seamlessly with GoToSocial.
GoToSocial works great with Tusky and Pinafore, but some other client applications still need work or have issues connecting to GoToSocial. We're tracking them [right here](https://github.com/superseriousbusiness/gotosocial/projects/5). It's our goal to make any app that's compatible with the Mastodon API work seamlessly with GoToSocial.
### Federation Issues
@ -60,13 +60,9 @@ For bugs and feature requests, please check to see if there's [already an issue]
## Credits
### Image Attribution and Licensing
### Image Attribution
Sloth logo by [Anna Abramek](https://abramek.art/).
<a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png" /></a><br />The GoToSocial sloth mascot is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">Creative Commons Attribution-ShareAlike 4.0 International License</a>.
For more information on GoToSocial image licensing, see [here](https://github.com/superseriousbusiness/gotosocial#image-attribution-and-licensing).
Sloth logo by [Anna Abramek](https://abramek.art/), Copyright (C) 2021-2023 the GoToSocial Authors.
### Developers
@ -84,27 +80,23 @@ Thanks to everyone who has used GtS, opened an issue, suggested something, given
## Sponsorship + Funding
**Please note: GoToSocial has NO CORPORATE SPONSORS and does not desire corporate sponsorship. In addition, we do not take donations from any of the following: adult websites, affiliate and review websites, casinos and gambling, insurance and financial products (credit), pharmacy products, SEO services and social media buying, VPN and proxy services, and essay writing services. Donations from such sources will be automatically rejected.**
### Crowdfunding
### OpenCollective
![open collective Standard Sloth badge](https://opencollective.com/gotosocial/tiers/standard-sloth/badge.svg?label=Standard%20Sloth&color=brightgreen) ![open collective Stable Sloth badge](https://opencollective.com/gotosocial/tiers/stable-sloth/badge.svg?label=Stable%20Sloth&color=green) ![open collective Special Sloth badge](https://opencollective.com/gotosocial/tiers/special-sloth/badge.svg?label=Special%20Sloth&color=yellowgreen) ![open collective Sugar Sloth badge](https://opencollective.com/gotosocial/tiers/sugar-sloth/badge.svg?label=Sugar%20Sloth&color=blue)
If you would like to donate to GoToSocial to keep the lights on during development, [you can do so via our OpenCollective page](https://opencollective.com/gotosocial#support)!
Currently, work on GoToSocial is funded through donations to our [OpenCollective](https://opencollective.com/) page.
If you would like to donate to GoToSocial to keep the lights on during development, [you can do so here](https://opencollective.com/gotosocial)! 💕 🦥 💕 Thank you!
### LiberaPay
![LiberaPay patrons](https://img.shields.io/liberapay/patrons/GoToSocial.svg?logo=liberapay) ![receives via LiberaPay](https://img.shields.io/liberapay/receives/GoToSocial.svg?logo=liberapay)
If you prefer, we also have an account on LiberaPay! You can find that [right here](https://liberapay.com/GoToSocial/).
Crowdfunded donations to our OpenCollective and Liberapay accounts go towards paying the core team, paying server costs, and paying for GtS art, design, and other bits and bobs.
### Provisos
💕 🦥 💕 Thank you!
### NLnet
<img src="https://nlnet.nl/logo/NGI/NGIZero-green.hex.svg" width="75" alt="NGIZero logo"/>
Combined with the above crowdfunding sources, 2023 Alpha development of GoToSocial is also funded by a 50,000 EUR grant from the [NGI0 Entrust Fund](https://nlnet.nl/entrust/), via [NLnet](https://nlnet.nl/). See [here](https://nlnet.nl/project/GoToSocial/#ack) for more details. The successful grant application is archived [here](https://github.com/superseriousbusiness/gotosocial/blob/main/archive/nlnet/2022-next-generation-internet-zero.md).
**Please note: GoToSocial has NO CORPORATE SPONSORS and does not desire corporate sponsorship. In addition, we do not take donations from any of the following: adult websites, affiliate and review websites, casinos and gambling, insurance and financial products (credit), pharmacy products, SEO services and social media buying, VPN and proxy services, and essay writing services. Donations from such sources will be automatically rejected.**
## License

View file

@ -0,0 +1,387 @@
# Advanced
Advanced configuration options for GoToSocial.
## Can I host my instance at `fedi.example.org` but have just `@example.org` in my username?
Yes, you can! This is useful when you have something like a personal page or blog at `example.org`, but you also want your fediverse account to have `example.org` in it to avoid confusing people, or just because it looks nicer than `fedi.example.org`.
Please note that you need to do this *BEFORE RUNNING GOTOSOCIAL* for the first time, or things will likely break.
### Step 1: Configure GoToSocial
This step is easy.
In the settings, GoToSocial differentiates between `host`--the address at which your instance is accessible--and `account-domain`--which is the domain you want to show in accounts.
Behold, from the example config.yaml file:
```yaml
# String. Hostname that this server will be reachable at. Defaults to localhost for local testing,
# but you should *definitely* change this when running for real, or your server won't work at all.
# DO NOT change this after your server has already run once, or you will break things!
# Examples: ["gts.example.org","some.server.com"]
# Default: "localhost"
host: "localhost"
# String. Domain to use when federating profiles. This is useful when you want your server to be at
# eg., "gts.example.org", but you want the domain on accounts to be "example.org" because it looks better
# or is just shorter/easier to remember.
#
# To make this setting work properly, you need to redirect requests at "example.org/.well-known/webfinger"
# to "gts.example.org/.well-known/webfinger" so that GtS can handle them properly.
#
# You should also redirect requests at "example.org/.well-known/nodeinfo" in the same way.
# An empty string (ie., not set) means that the same value as 'host' will be used.
#
# DO NOT change this after your server has already run once, or you will break things!
#
# Please read the appropriate section of the installation guide before you go messing around with this setting:
# https://docs.gotosocial.org/installation_guide/advanced/#can-i-host-my-instance-at-fediexampleorg-but-have-just-exampleorg-in-my-username
#
# Examples: ["example.org","server.com"]
# Default: ""
account-domain: ""
```
The first value, `host`, is simple. In our scenario of wanting to run the GtS instance at `fedi.example.org`, this should be set to, yep, `fedi.example.org`.
The second value, `account-domain` should be set to `example.org`, to indicate that that's the domain we want accounts to be displayed with.
IMPORTANT: `account-domain` must be a *parent domain* of `host`, and `host` must be a *subdomain* of `account-domain`. So if your `host` is `fedi.example.org`, your `account-domain` cannot be `somewhere.else.com` or `example.com`, it **has to be** `example.org`.
### Step 2: Redirect from `example.org` to `fedi.example.org`
The next step is more difficult: we need to ensure that when remote instances search for the user `@user@example.org` via webfinger, they end up being pointed towards `fedi.example.org`, where our instance is actually hosted.
Of course, we don't want to redirect *all* requests from `example.org` to `fedi.example.org` because that negates the purpose of having a separate domain in the first place, so we need to be specific.
In the config.yaml above, there are two endpoints mentioned, both of which we need to redirect: `/.well-known/webfinger` and `/.well-known/nodeinfo`.
Assuming we have an [nginx](https://nginx.org) reverse proxy running on `example.org`, we can get the redirect behavior we want by adding the following to the nginx config for `example.org`:
```nginx
http {
server {
listen 80;
listen [::]:80;
server_name example.org;
location /.well-known/webfinger {
rewrite ^.*$ https://fedi.example.org/.well-known/webfinger permanent;
}
location /.well-known/nodeinfo {
rewrite ^.*$ https://fedi.example.org/.well-known/nodeinfo permanent;
}
# The rest of our nginx config ...
}
}
```
The above configuration [rewrites](https://www.nginx.com/blog/creating-nginx-rewrite-rules/) queries to `example.org/.well-known/webfinger` and `example.org/.well-known/nodeinfo` to their `fedi.example.org` counterparts, which means that query information is preserved, making it easier to follow the redirect.
### Step 3: What now?
Once you've done steps 1 and 2, proceed as normal with the rest of your GoToSocial installation.
### Supplemental: how does this work?
With the configuration we put in place in the steps above, when someone from another instance looks up `@user@example.org`, their instance will perform a webfinger request to `example.org/.well-known/webfinger?resource:acct=user@example.org` in order to discover a link to an ActivityPub representation of that user's account. They will then be redirected to `https://fedi.example.org/.well-known/webfinger?resource:acct=user@example.org`, and their query will be resolved.
The webfinger response returned by GoToSocial (and indeed Mastodon, and other ActivityPub implementations) contains the desired account domain in the `subject` part of the response, and provides links to aliases that should be used to query the account.
Here's an example of this working for the `superseriousbusiness.org` GoToSocial instance, which is hosted at `gts.superseriousbusiness.org`.
Curl query:
```bash
curl -v 'https://superseriousbusiness.org/.well-known/webfinger?resource=acct:@gotosocial@superseriousbusiness.org'
```
Response:
```text
> GET /.well-known/webfinger?resource=acct:@gotosocial@superseriousbusiness.org HTTP/2
> Host: superseriousbusiness.org
> user-agent: curl/7.68.0
> accept: */*
>
< HTTP/2 301
< content-type: text/html
< date: Thu, 17 Nov 2022 11:10:39 GMT
< location: https://gts.superseriousbusiness.org/.well-known/webfinger?resource=acct:@gotosocial@superseriousbusiness.org
< server: nginx/1.20.1
< content-length: 169
<
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.20.1</center>
</body>
</html>
```
If we follow the redirect and make a query to the specified `location` as follows:
```bash
curl -v 'https://gts.superseriousbusiness.org/.well-known/webfinger?resource=acct:@gotosocial@superseriousbusiness.org'
```
Then we get the following response:
```json
{
"subject": "acct:gotosocial@superseriousbusiness.org",
"aliases": [
"https://gts.superseriousbusiness.org/users/gotosocial",
"https://gts.superseriousbusiness.org/@gotosocial"
],
"links": [
{
"rel": "http://webfinger.net/rel/profile-page",
"type": "text/html",
"href": "https://gts.superseriousbusiness.org/@gotosocial"
},
{
"rel": "self",
"type": "application/activity+json",
"href": "https://gts.superseriousbusiness.org/users/gotosocial"
}
]
}
```
In the above response, note that the `subject` of the response contains the desired account-domain of `superseriousbusiness.org`, whereas the links contain the actual host value of `gts.superseriousbusiness.org`.
## Can I make my GoToSocial instance use a proxy (http, https, socks5) for outgoing requests?
Yes! GoToSocial supports canonical environment variables for doing this: `HTTP_PROXY`, `HTTPS_PROXY` and `NO_PROXY` (or the lowercase versions thereof). `HTTPS_PROXY` takes precedence over `HTTP_PROXY` for https requests.
The http client that GoToSocial uses will be initialized with the appropriate proxy.
The environment values may be either a complete URL or a `host[:port]`, in which case the "http" scheme is assumed. The schemes "http", "https", and "socks5" are supported.
## Application sandboxing
Although GoToSocial does not currently have any known vulnerabilities, it's
always a good idea to be proactive about security. One way you can help protect
your instance is to run it in a *sandbox* -- an environment that constrains the
actions a program can perform in order to limit the impact of a future exploit.
[Using Docker](../../installation_guide/docker) to run GoToSocial can work as a
(limited) sandboxing mechanism. For Linux installations, [Linux Security
Modules](https://en.wikipedia.org/wiki/Linux_Security_Modules) such as
[AppArmor](https://www.apparmor.net/) and
[SELinux](https://en.wikipedia.org/wiki/Security-Enhanced_Linux) work as a
complementary mechanism that typically provide stronger protections. You should
use
- **AppArmor** if you're running GoToSocial on Debian, Ubuntu, or OpenSUSE, and
- **SELinux** if you're using CentOS, RHEL, or Rocky Linux.
For other Linux distributions, you will need to look up what Linux Security
Modules are supported by your kernel.
!!! note
GoToSocial is currently alpha software, and as more features are implemented
these security policies may quickly become outdated. You may find that using
AppArmor or SELinux causes GoToSocial to fail in unexpected ways until GTS
becomes stable.
!!! caution
Sandboxing is an _additional_ security mechanism to help defend against
certain kinds of attacks; it _is not_ a replacement for good security
practices.
### AppArmor
For Linux distributions supporting AppArmor, there is an AppArmor profile
available in `example/apparmor/gotosocial` that you can use to confine your
GoToSocial instance. If you're using a server (such as a VPS) to deploy
GoToSocial, you can install the AppArmor profile by downloading it and copying
it into the `/etc/apparmor.d/` directory:
```bash
wget https://raw.githubusercontent.com/superseriousbusiness/gotosocial/main/example/apparmor/gotosocial
sudo install -o root -g root gotosocial /etc/apparmor.d/gotosocial
sudo apparmor_parser -Kr /etc/apparmor.d/gotosocial
```
If you're using Docker Compose, you should add the following `security_opt`
section to your Compose configuration file:
```yaml
services:
gotosocial:
...
security_opt:
- apparmor=gotosocial
```
If you're running GoToSocial as a Systemd service, you should instead add this
line under `[Service]`:
```ini
[Service]
...
AppArmorProfile=gotosocial
```
For other deployment methods (e.g. a managed Kubernetes cluster), you should
review your platform's documentation for how to deploy an application with an
AppArmor profile.
#### Disabling the AppArmor profile
If enabling the AppArmor profile causes your instance to experience issues, you
can uninstall it from the system as follows:
```
sudo apparmor_parser -R /etc/apparmor.d/gotosocial
sudo rm -vi /etc/apparmor.d/gotosocial
```
You will also want to remove any changes you made to your Compose configuration
or Systemd service file to enable the profile.
### SELinux
!!! note
Currently, this SELinux policy only works for the [binary installation
method](../../installation_guide/binary).
If SELinux is available on your system, you can optionally install [SELinux
policy](https://github.com/lzap/gotosocial-selinux) to further improve security.
## nginx
This section contains a number of additional things for configuring nginx.
### Extra Hardening
If you want to harden up your NGINX deployment with advanced configuration options, there are many guides online for doing so ([for example](https://beaglesecurity.com/blog/article/nginx-server-security.html)). Try to find one that's up to date. Mozilla also publishes best-practice ssl configuration [here](https://ssl-config.mozilla.org/).
### Caching Webfinger
It's possible to use nginx to cache the webfinger responses. This may be useful in order to ensure clients still get a response on the webfinger endpoint even if GTS is (temporarily) down.
You'll need to configure two things:
* A cache path
* An additional `location` block for webfinger
First, the cache path which needs to happen in the `http` section, usually inside your `nginx.conf`:
```nginx.conf
http {
... there will be other things here ...
proxy_cache_path /var/cache/nginx keys_zone=ap_webfinger:10m inactive=1w;
}
```
This configures a cache of 10MB whose entries will be kept up to one week if they're not accessed. The zone is named `ap_webfinger` but you can name it whatever you want. 10MB is a lot of cache keys, you can probably use a much smaller value on small instances.
Second, actually use the cache for webfinger:
```nginx.conf
server {
server_name example.org;
location /.well-known/webfinger {
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache ap_webfinger;
proxy_cache_background_update on;
proxy_cache_key $scheme://$host$uri$is_args$query_string;
proxy_cache_valid 200 10m;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504 http_429;
proxy_cache_lock on;
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://localhost:8080;
}
location / {
proxy_pass http://localhost:8080/;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
client_max_body_size 40M;
listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/example.org/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/example.org/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
```
The `proxy_pass` and `proxy_set_header` are mostly the same, but the `proxy_cache*` entries warrant some explanation:
* `proxy_cache ap_webfinger` tells it to use the `ap_webfinger` cache zone we previously created. If you named it something else, you should change this value
* `proxy_cache_background_update on` means nginx will try and refresh a cached resource that's about to expire in the background, to ensure it has a current copy on disk
* `proxy_cache_key` is configured in such a way that it takes the query string into account for caching. So a request for `.well-known/webfinger?acct=user1@example.org` and `.well-known/webfinger?acct=user2@example.org` are not seen as the same
* `proxy_cache_valid 200 10m;` means we only cache 200 responses from GTS and for 10 minutes. You can add additional lines of these, like `proxy_cache_valid 404 1m;` to cache 404 responses for 1 minute
* `proxy_cache_use_stale` tells nginx it's allowed to use a stale cache entry (so older than 10 minutes) in certain cases
* `proxy_cache_lock on` means that if a resource is not cached and there's multiple concurrent requests for them, the queries will be queued up so that only one request goes through and the rest is then answered from cache
* `add_header X-Cache-Status $upstream_cache_status` will add an `X-Cache-Status` header to the response so you can check if things are getting cached. You can remove this.
Tweaking `proxy_cache_use_stale` is how you can ensure webfinger responses are still answered even if GTS itself is down. The provided configuration will serve a stale response in case there's an error proxying to GTS, if our connection to GTS times out, if GTS returns a 5xx status code or if GTS returns 429 (Too Many Requests). The `updating` value says that we're allowed to serve a stale entry if nginx is currently in the process of refreshing its cache. Because we configured `inactive=1w` in the `proxy_cache_path` directive, nginx may serve a response up to one week old if the conditions in `proxy_cache_use_stale` are met.
### Serving static assets
By default, GTS will serve assets like the CSS and fonts for the web UI as well as attachments for statuses. However it's very simple to have nginx do this instead and offload GTS from that responsibility. Nginx can generally do a faster job at this too since it's able to use newer functionality in the OS that the Go runtime hasn't necessarily adopted yet.
There are 2 paths that nginx can handle for us:
* `/assets` which contains fonts, CSS, images etc. for the web UI
* `/fileserver` which serves attachments for status posts when using the local storage backend
For `/assets` we'll need the value of `web-asset-base-dir` from the configuration, and for `/fileserver` we'll want `storage-local-base-path`. You can then adjust your nginx configuration like this:
```nginx.conf
server {
server_name example.org;
location /assets/ {
alias web-asset-base-dir/;
autoindex off;
expires 5m;
add_header Cache-Control "public";
}
location /fileserver/ {
alias storage-local-base-path/;
autoindex off;
expires max;
add_header Cache-Control "public, immutable";
}
location / {
proxy_pass http://localhost:8080/;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
client_max_body_size 40M;
listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/example.org/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/example.org/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
```
The trailing slashes in the new `location` directives and the `alias` are significant, do not remove those. The `expires` directive adds the necessary headers to inform the client how long it may cache the resource. For assets, which may change on each release, 5 minutes is used in this example. For attachments, which should never change once they're created, `max` is used instead setting the cache expiry to the 31st of December 2037. For other options, see the nginx documentation on the [`expires` directive](https://nginx.org/en/docs/http/ngx_http_headers_module.html#expires). Nginx does not add cache headers to 4xx or 5xx response codes so a failure to fetch an asset won't get cached by clients. The `autoindex off` directive tells nginx to not serve a directory listing. This should be the default but it doesn't hurt to be explicit. The added `add_header` lines set additional options for the `Cache-Control` header:
* `public` is used to indicate that anyone may cache this resource
* `immutable` is used to indicate this resource will never change while it is fresh (it's before the end of the expires) allowing clients to forego conditional requests to revalidate the resource during that timespan

View file

@ -1,10 +1,36 @@
# Apache HTTP Server
# Reverse proxy with Apache HTTP Server
In order to use Apache as a reverse proxy for GoToSocial you'll need to have it installed on your server. If you intend for the Apache instance to also handle TLS, you'll need to [provision TLS certificates](../../advanced/certificates.md) too.
## Requirements
Apache is [packaged for many distributions](https://repology.org/project/apache/versions). It's very likely you can install it with your distribution's package manager. You can also run Apache using a container runtime with the [official Apache image](https://hub.docker.com/_/httpd) that's published to the Docker Hub.
For this you will need the Apache HTTP Server.
In this guide we'll also show how to use certbot to provision the TLS certificates. It too is [packaged in many distributions](https://repology.org/project/certbot/versions) but many distributions tend to ship fairly old versions of certbot. If you run into trouble it may be worth considering using the [container image](https://hub.docker.com/r/certbot/certbot) instead.
That is a fairly popular package so your distro will probably have it.
### Ubuntu
```bash
sudo apt install apache2
```
### Arch
```bash
sudo pacman -S apache
```
### OpenSuse
```bash
sudo zypper install apache2
```
### Install modules
You'll also need to install additional modules for Apache HTTP Server. You can do that with the following command:
```bash
sudo a2enmod proxy_http md ssl headers rewrite
```
## Configure GoToSocial
@ -28,28 +54,7 @@ sudo systemctl restart gotosocial.service
Or if you don't have a systemd service just restart it manually.
## Set up Apache
### Required Apache modules
You need to ensure you have a number of Apache modules installed and enabled. All these modules *should* ship with your distribution's Apache package, but they may have been split out into separate packages.
You can check which modules you have installed with `apachectl -M`.
You'll need to have the following modules loaded:
* `proxy_http` to proxy HTTP requests to GoToSocial
* `ssl` to handle SSL/TLS
* `headers` to manipulate HTTP request and response headers
* `rewrite` to rewrite HTTP requests
* `md` for Lets Encrypt, available since 2.4.30
On Debian, Ubuntu and openSUSE, you can use the [`a2enmod`](https://manpages.debian.org/bookworm/apache2/a2enmod.8.en.html) utility to load any additional modules. For the Red Hat/CentOS family of distributions, you'll need to add a [`LoadModule` directive](https://httpd.apache.org/docs/2.4/mod/mod_so.html#loadmodule) to your Apache configuration instead.
### TLS with mod_md
!!! note
`mod_md` is only available since Apache 2.4.30 and still considered experimental. It works well enough in practice and is the most convenient method.
## Set up Apache HTTP Server with SSL managed using MD module
Now we'll configure Apache HTTP Server to serve GoToSocial requests.
@ -91,29 +96,6 @@ MDCertificateAgreement accepted
</VirtualHost>
```
or, if you have [Apache httpd 2.4.47+](https://httpd.apache.org/docs/2.4/mod/mod_proxy.html#protoupgrade), you can get rid of both `mod_rewrite` and `mod_proxy_wstunnel` and simplify the whole config to:
```apache
MDomain example.com auto
MDCertificateAgreement accepted
<VirtualHost *:80 >
ServerName example.com
</VirtualHost>
<VirtualHost *:443>
ServerName example.com
SSLEngine On
ProxyPreserveHost On
# set to 127.0.0.1 instead of localhost to work around https://stackoverflow.com/a/52550758
ProxyPass / http://127.0.0.1:8080/ upgrade=websocket
ProxyPassReverse / http://127.0.0.1:8080/
RequestHeader set "X-Forwarded-Proto" expr=https
</VirtualHost>
```
Again, replace occurrences of `example.com` in the above config file with the hostname of your GtS server. If your domain name is `gotosocial.example.com`, then `gotosocial.example.com` would be the correct value.
You should also change `http://127.0.0.1:8080` to the correct address and port (if it's not on `127.0.0.1:8080`) of your GtS server. For example, if you're running GoToSocial on another machine with the local ip of `192.168.178.69` and on port `8080` then `http://192.168.178.69:8080/` would be the correct value.
@ -122,7 +104,7 @@ You should also change `http://127.0.0.1:8080` to the correct address and port (
`ProxyPreserveHost On` is essential: It guarantees that the proxy and the GoToSocial speak of the same Server name. If not, GoToSocial will build the wrong authentication headers, and all attempts at federation will be rejected with 401 Unauthorized.
By default, apache sets `X-Forwarded-For` in forwarded requests. To make this and rate limiting work, set the `trusted-proxies` configuration variable. See the [rate limiting](../../api/ratelimiting.md) and [general configuration](../../configuration/general.md) docs
By default, apache sets `X-Forwarded-For` in forwarded requests. To make this and rate limiting work, set the `trusted-proxies` configuration variable. See the [rate limiting](../api/ratelimiting.md) and [general configuration](../configuration/general.md) docs
Save and close the config file.
@ -164,10 +146,7 @@ If this happens, you'll need to do one (or all) of the below:
1. Update `/etc/apache2/sites-enabled/000-default.conf` and change the `ServerAdmin` value to a valid email address (then reload Apache HTTP Server).
2. Add the line `MDContactEmail your.email.address@whatever.com` below the `MDomain` line in `/etc/apache2/sites-available/example.com.conf`, replacing `your.email.address@whatever.com` with a valid email address, and `example.com` with your GtS host name.
### TLS with externally managed certificates
!!! note
We have additional documentation on how to [provision TLS certificates](../../advanced/certificates.md) that also provides links to additional content and tutorials for different distributions that may be good to review.
## Set up Apache HTTP Server with SSL managed manually or by an external software (e.g. Certbot or acme.sh)
If you prefer to have a manual setup or setting SSL using a different service to manage it (Certbot, etc), then you can use a simpler setup for your Apache HTTP Server.
@ -204,7 +183,7 @@ Again, replace occurrences of `example.com` in the above config file with the ho
You should also change `http://127.0.0.1:8080` to the correct address and port (if it's not on `127.0.0.1:8080`) of your GtS server. For example, if you're running GoToSocial on another machine with the local ip of `192.168.178.69` and on port `8080` then `http://192.168.178.69:8080/` would be the correct value.
`Rewrite*` directives are needed to ensure that Websocket streaming connections also work. See the [websocket](websocket.md) document for more information on this.
`Rewrite*` directives are needed to ensure that Websocket streaming connections also work. See the [websocket](./websocket.md) document for more information on this.
`ProxyPreserveHost On` is essential: It guarantees that the proxy and the GoToSocial speak of the same Server name. If not, GoToSocial will build the wrong authentication headers, and all attempts at federation will be rejected with 401 Unauthorized.

View file

@ -1,8 +1,8 @@
# Bare metal
# Binary Installation From Release
This guide walks you through getting GoToSocial up and running on bare metal using the official binary releases.
This is the binary installation guide for GoToSocial. It is assumed that you already have a [properly configured VPS running in the cloud, or a suitable homeserver that is accessible with port forwarding](index.md).
## Prepare VPS
## 1: Prepare VPS
In a terminal on the VPS or your homeserver, make the directory that GoToSocial will run from, the directory it will use as storage, and the directory it will store LetsEncrypt certificates in:
@ -12,7 +12,7 @@ mkdir /gotosocial && mkdir /gotosocial/storage && mkdir /gotosocial/storage/cert
If you don't have root permissions on the machine, use something like `~/gotosocial` instead.
## Download Release
## 2: Download Release
In a terminal on the VPS or your homeserver, cd into the base directory for GoToSocial you just created above:
@ -38,7 +38,7 @@ tar -xzf gotosocial_0.5.2_linux_amd64.tar.gz
This will put the `gotosocial` binary in your current directory, in addition to the `web` folder, which contains assets for the web frontend, and an `example` folder, which contains a sample configuration file.
## Edit Configuration File
## 3. Edit Configuration File
Copy the configuration file from the example folder into your current directory:
@ -56,9 +56,9 @@ Now open the file in your text editor of choice so that you can set some importa
- Set `letsencrypt-enabled` to `true`.
- Set `letsencrypt-cert-dir` to the certificate storage directory you created above (eg., `/gotosocial/storage/certs`).
The above options assume you're using SQLite as your database. If you want to use Postgres instead, see [here](../../configuration/database.md) for the config options.
The above options assume you're using SQLite as your database. If you want to use Postgres instead, see [here](../configuration/database.md) for the config options.
## Run the Binary
## 4: Run the Binary
You can now run the binary.
@ -72,15 +72,40 @@ The server should now start up and you should be able to access the splash page
Note that for this example we're assuming that we're allowed to run on port 443 (standard https port), and that nothing else is running on this port.
## Create your user
## 5: Create your user
You can use the GoToSocial binary to also create and promote your user account. This is all documented in our [Creating users](../user_creation.md) guide.
You can use the GoToSocial binary to also create and promote your user account.
## Login
Run the following command to create a new account:
You should now be able to log in to your instance using the email address and password of the account you just created. We recommend using [Semaphore](https://semaphore.social) or [Tusky](https://tusky.app) for this.
```bash
./gotosocial --config-path ./config.yaml admin account create --username some_username --email some_email@whatever.org --password 'SOME_PASSWORD'
```
## (Optional) Enable the systemd service
In the above command, replace `some_username` with your desired username, `some_email@whatever.org` with the email address you want to associate with your account, and `SOME_PASSWORD` with a secure password.
If you are running a version older than 0.6.0, you will need to manually confirm as well:
```bash
./gotosocial --config-path ./config.yaml admin account confirm --username some_username
```
Replace `some_username` with the username of the account you just created.
If you want your user to have admin rights, you can promote them using a similar command:
```bash
./gotosocial --config-path ./config.yaml admin account promote --username some_username
```
Replace `some_username` with the username of the account you just created.
## 6. Login
You should now be able to log in to your instance using the email address and password of the account you just created. We recommend using [Pinafore](https://pinafore.social) or [Tusky](https://tusky.app) for this.
## 7. \[Optional\] Enable the systemd service
If you don't like manually starting GoToSocial on every boot you might want to create a systemd service that does that for you.
@ -118,6 +143,6 @@ After you're done enable the service:
sudo systemctl enable --now gotosocial.service
```
## (Optional) Reverse proxy
## 8. \[Optional\] Reverse proxy
If you want to run other webservers on port 443 or want to add an additional layer of security you might want to use a [reverse proxy](../reverse_proxy/index.md). We have guides available for a couple of popular open source options and will gladly take pull requests to add more.
If you want to run other webservers on port 443 or want to add an additional layer of security you might want to use [nginx](./nginx.md), [Caddy](./caddy.md) or [Apache httpd](./apache-httpd.md) as reverse proxy

View file

@ -1,4 +1,4 @@
# Caddy 2
# Reverse proxy with Caddy 2
## Requirements
@ -79,7 +79,7 @@ example.org {
}
```
By default, caddy sets `X-Forwarded-For` in forwarded requests. To make this and rate limiting work, set the `trusted-proxies` configuration variable. See the [rate limiting](../../api/ratelimiting.md) and [general configuration](../../configuration/general.md) docs
By default, caddy sets `X-Forwarded-For` in forwarded requests. To make this and rate limiting work, set the `trusted-proxies` configuration variable. See the [rate limiting](../api/ratelimiting.md) and [general configuration](../configuration/general.md) docs
For advanced configuration check the [reverse_proxy directive](https://caddyserver.com/docs/caddyfile/directives/reverse_proxy) at the Caddy documentation.

Some files were not shown because too many files have changed in this diff Show more