mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-10-29 15:32:25 -05:00
Compare commits
342 commits
v0.18.0-rc
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5fd52369c9 | ||
|
|
baf2c54730 | ||
|
|
b012a81f66 | ||
|
|
c6044d0142 | ||
|
|
03fc6eaf39 | ||
|
|
5b95636993 | ||
|
|
259fa1ffac | ||
|
|
57cb4fe748 | ||
|
|
ff950e94bb | ||
|
|
e7cd8bb43e | ||
|
|
bd1c43d55e | ||
|
|
dfdf06e4ad | ||
|
|
3db2d42247 | ||
|
|
121677754c | ||
|
|
602022701b | ||
|
|
82216281ce | ||
|
|
6607e1c944 | ||
|
|
e81bcb5171 | ||
|
|
6801ce299a | ||
|
|
247733aef4 | ||
|
|
5533fbc1f8 | ||
|
|
11f39bead0 | ||
|
|
0a32fc0980 | ||
|
|
10b8d270f2 | ||
|
|
882d07db5f | ||
|
|
d9ee6cddea | ||
|
|
f9cb086c53 | ||
|
|
754b7be9cf | ||
|
|
33fed81a8d | ||
|
|
c949b9f2d1 | ||
|
|
a6429b5410 | ||
|
|
9b7db51436 | ||
|
|
4b6c65fc15 | ||
|
|
19784e381b | ||
|
|
ed3035699f | ||
|
|
78defcd916 | ||
|
|
5a54e7156b | ||
|
|
9cb177cd8a | ||
|
|
3efb338ca9 | ||
|
|
380bfd0289 | ||
|
|
383e41e3e5 | ||
|
|
a79f83cbde | ||
|
|
8c619d51b5 | ||
|
|
1144ac037f | ||
|
|
3fad524089 | ||
|
|
a6bb45e5e4 | ||
|
|
e9b7e977a5 | ||
|
|
12e3cb71c1 | ||
|
|
8e76c9ab57 | ||
|
|
fb2ef90ec5 | ||
|
|
7f8cb204cd | ||
|
|
1edc0f7b3c | ||
|
|
660cf2c94c | ||
|
|
cead741c16 | ||
|
|
e565855540 | ||
|
|
67100809b3 | ||
|
|
7af9117e0d | ||
|
|
96c05a90a2 | ||
|
|
f301bd5abf | ||
|
|
7e45168d33 | ||
|
|
ac032ff6ab | ||
|
|
e3dfd88893 | ||
|
|
30cd1cd9eb | ||
|
|
c00cad2ceb | ||
|
|
eb60081985 | ||
|
|
a4b54aa935 | ||
|
|
94cbe1120e | ||
|
|
a97a15fd10 | ||
|
|
352353ce7a | ||
|
|
dcfc9b7885 | ||
|
|
c8a4ce9a88 | ||
|
|
07f61a13de | ||
|
|
66e1ec14aa | ||
|
|
79914bdbf7 | ||
|
|
bf46b87343 | ||
|
|
7a5144b4cc | ||
|
|
4f2aa792b3 | ||
|
|
8b0ea56027 | ||
|
|
7712885038 | ||
|
|
5fbaf5b7be | ||
|
|
39b11dbfb6 | ||
|
|
700bd69828 | ||
|
|
996da6e029 | ||
|
|
9d5af6c3dc | ||
|
|
56d1248a85 | ||
|
|
80191348ba | ||
|
|
38ff88f006 | ||
|
|
bfc8c31e5f | ||
|
|
29d481d769 | ||
|
|
8b615391e5 | ||
|
|
b1c0eca1d8 | ||
|
|
7d50aec6dc | ||
|
|
a9b2d4ee35 | ||
|
|
76e7fd62d2 | ||
|
|
726584287a | ||
|
|
5717ce6f3c | ||
|
|
fc074d2357 | ||
|
|
933a9091eb | ||
|
|
e87681d433 | ||
|
|
d7f967cbb5 | ||
|
|
1dc79c9586 | ||
|
|
4ad17788cd | ||
|
|
d3f2c2c765 | ||
|
|
153f6feecd | ||
|
|
37f9a9fa94 | ||
|
|
3f1c3c0dac | ||
|
|
54a0af34fb | ||
|
|
bd4fe7081e | ||
|
|
01e3765268 | ||
|
|
32738d83a8 | ||
|
|
b13a6437ff | ||
|
|
77eddea3af | ||
|
|
a37dd59d1f | ||
|
|
118d4e4d03 | ||
|
|
43f1c6d872 | ||
|
|
be6d80c020 | ||
|
|
6d511d696b | ||
|
|
5097e6d278 | ||
|
|
ee180a2359 | ||
|
|
025ca487cf | ||
|
|
faed35c938 | ||
|
|
a82d574acc | ||
|
|
3b46eb6a9e | ||
|
|
d9e8d844e1 | ||
|
|
0e698a49fb | ||
|
|
3ff6f6e421 | ||
|
|
2c73bb3602 | ||
|
|
6c1b674278 | ||
|
|
f3c4ea0106 | ||
|
|
143febb318 | ||
|
|
311d9a1697 | ||
|
|
fd64a1e264 | ||
|
|
326e04283a | ||
|
|
b6ff55662e | ||
|
|
20aad9be0f | ||
|
|
00e58c60cd | ||
|
|
d5c9c4adc1 | ||
|
|
ec4d4d0115 | ||
|
|
3a29a59e55 | ||
|
|
3cff4b2d7d | ||
|
|
cfe6336b6e | ||
|
|
3ba49aed45 | ||
|
|
ca12742a7a | ||
|
|
5925644ad3 | ||
|
|
3fedff3a5a | ||
|
|
d3c67ad642 | ||
|
|
8480a75808 | ||
|
|
6b6946f42a | ||
|
|
61137e6c41 | ||
|
|
7bd4bed558 | ||
|
|
d2f13e7564 | ||
|
|
8c1511a494 | ||
|
|
cfa83fb44c | ||
|
|
700b7eaab7 | ||
|
|
4c96e2571d | ||
|
|
1f0c261fd2 | ||
|
|
7e105f98ed | ||
|
|
b2a469d6b6 | ||
|
|
6acf56cde9 | ||
|
|
7d74548a91 | ||
|
|
ebf64eb0db | ||
|
|
8264b63337 | ||
|
|
e464de1322 | ||
|
|
cf93d3af0a | ||
|
|
57fc267b5c | ||
|
|
90a5425fe9 | ||
|
|
4a6b357501 | ||
|
|
73aa62581e | ||
|
|
ecbdc4227b | ||
|
|
ad71066973 | ||
|
|
83184da6e7 | ||
|
|
89df9f3b21 | ||
|
|
f3f1854359 | ||
|
|
056c67f396 | ||
|
|
211192c482 | ||
|
|
c15002d76e | ||
|
|
bad427e7f0 | ||
|
|
4d6408015b | ||
|
|
6c87918635 | ||
|
|
bf10ca0203 | ||
|
|
1480f22aea | ||
|
|
1bad36b637 | ||
|
|
90f5004db0 | ||
|
|
ac01652de9 | ||
|
|
ef0f8a55c6 | ||
|
|
31628019fe | ||
|
|
14f15b321b | ||
|
|
4fe5dfae56 | ||
|
|
efc79528d2 | ||
|
|
d8c4d9fc5a | ||
|
|
2b82fa7481 | ||
|
|
2d91eed4ed | ||
|
|
4c8ff7d1b2 | ||
|
|
afb4279d27 | ||
|
|
436765a6a2 | ||
|
|
457ca3c9d3 | ||
|
|
7dae3364fa | ||
|
|
8bc0b06105 | ||
|
|
d3e9a9e563 | ||
|
|
d5c3f44bb9 | ||
|
|
9b92175239 | ||
|
|
93c3c153f0 | ||
|
|
5bfccdad3c | ||
|
|
346962beb5 | ||
|
|
ab10266bff | ||
|
|
152bcb43b6 | ||
|
|
85eb192669 |
||
|
|
f7323c065a |
||
|
|
6a6a499333 |
||
|
|
ffde1b150f |
||
|
|
68ed7aba25 |
||
|
|
768ee70b88 |
||
|
|
ab2044ef02 |
||
|
|
9241a0c337 |
||
|
|
d6232663e3 |
||
|
|
355482baf6 |
||
|
|
d1abbd0290 |
||
|
|
356c2adddc |
||
|
|
c670493ad9 |
||
|
|
f170bbcd49 |
||
|
|
075cae3d55 |
||
|
|
0992ffc057 |
||
|
|
a87be80c90 |
||
|
|
e9f6d186dc |
||
|
|
d308fd0d0a |
||
|
|
2863561c89 |
||
|
|
14899733c8 |
||
|
|
2fce027808 |
||
|
|
d694666436 |
||
|
|
245d6db979 |
||
|
|
0ce9bed3d1 |
||
|
|
2b98432ca2 |
||
|
|
5b2fe1f9a0 |
||
|
|
b510f3c539 |
||
|
|
f5ce219844 |
||
|
|
67fc1fd904 |
||
|
|
36758ea0de |
||
|
|
a44065726c |
||
|
|
a07ca92752 |
||
|
|
6ff0d3355c |
||
|
|
f8d17a7e76 |
||
|
|
df5d8b42da |
||
|
|
94963b0f24 |
||
|
|
51b9ef5c34 |
||
|
|
c803620531 |
||
|
|
c8a780e12a |
||
|
|
eb4114bf12 |
||
|
|
b51aa23458 |
||
|
|
1e59795ff4 |
||
|
|
be9e307418 |
||
|
|
e032c959e1 |
||
|
|
b1a4d54c14 |
||
|
|
19cfa8d126 |
||
|
|
365b575341 |
||
|
|
6f24205a26 |
||
|
|
bce643286c |
||
|
|
e0ea77b730 |
||
|
|
920e20c2d2 |
||
|
|
4232d61782 |
||
|
|
2cc5d6269d |
||
|
|
e263d23622 |
||
|
|
8ae2440da3 |
||
|
|
650be1e8d0 |
||
|
|
b184432331 |
||
|
|
db4b857159 |
||
|
|
6473886c8e |
||
|
|
57c5f68e1a |
||
|
|
b0873972ec |
||
|
|
fdf23a91de |
||
|
|
9c31e213ca |
||
|
|
0850a8f646 |
||
|
|
3949117be0 |
||
|
|
85fb63f46f |
||
|
|
13e7ac3e6a |
||
|
|
d49abd7bd7 |
||
|
|
b6e481d63e |
||
|
|
f46e490c30 |
||
|
|
1bf40e755c |
||
|
|
27171a78de |
||
|
|
4af8d1a2cb |
||
|
|
f7e5f31c6b |
||
|
|
a844f322ee |
||
|
|
18c8f85a30 |
||
|
|
a2caa5fdbb |
||
|
|
808031ca01 |
||
|
|
e7b4690318 |
||
|
|
cecb1fd009 |
||
|
|
657e064bf6 |
||
|
|
59aac290e4 |
||
|
|
d5847e2d2b |
||
|
|
d3c3d34aae |
||
|
|
85ee7cca47 |
||
|
|
daf60f69a0 |
||
|
|
da462afce2 |
||
|
|
f30bb549aa |
||
|
|
6c5d369b05 |
||
|
|
0c49d5abb8 |
||
|
|
bad12a62e6 |
||
|
|
95f88e5d93 |
||
|
|
0c72282559 |
||
|
|
3db5bfa0f2 |
||
|
|
252a17a691 |
||
|
|
85e17b3e78 |
||
|
|
719c925299 |
||
|
|
35e94c8abd |
||
|
|
98c4cae89a |
||
|
|
d8113c11e4 |
||
|
|
424f62dd70 |
||
|
|
69461c461b |
||
|
|
829143d263 |
||
|
|
ee60732cf7 |
||
|
|
ab7ec43988 |
||
|
|
24da574684 |
||
|
|
1b37944f8b |
||
|
|
c80810eae8 |
||
|
|
67a2b3650c |
||
|
|
ddd9210614 |
||
|
|
861b5cd920 |
||
|
|
0e2e8e54ab |
||
|
|
8488ac9286 |
||
|
|
49c12636c6 |
||
|
|
d8bb1c391b |
||
|
|
5d0e3d9c35 |
||
|
|
e2e185d10b |
||
|
|
4c9901fc03 |
||
|
|
e78e817057 |
||
|
|
a7485ae251 |
||
|
|
b4bb97225e |
||
|
|
0118e03cda |
||
|
|
d73acc70d5 |
||
|
|
46862171e0 |
||
|
|
1f38b7b48f |
||
|
|
8bc502ba62 |
||
|
|
eb720241da |
||
|
|
f734a94c1c |
||
|
|
e1ec904dd3 |
||
|
|
d2cf9de726 |
||
|
|
96d39155f2 |
||
|
|
1c049ba295 |
||
|
|
d8f96f81d6 |
||
|
|
fd670c6a27 |
||
|
|
c9de6c9a1e |
4735 changed files with 851808 additions and 500886 deletions
223
.drone.yml
223
.drone.yml
|
|
@ -1,223 +0,0 @@
|
||||||
---
|
|
||||||
### Drone configuration file for GoToSocial.
|
|
||||||
### Connects to https://drone.superseriousbusiness.org to perform testing, linting, and automatic builds/pushes to docker.
|
|
||||||
###
|
|
||||||
### For documentation on drone, see: https://docs.drone.io/
|
|
||||||
### For documentation on drone docker pipelines in particular: https://docs.drone.io/pipeline/docker/overview/
|
|
||||||
kind: pipeline
|
|
||||||
type: docker
|
|
||||||
name: default
|
|
||||||
|
|
||||||
steps:
|
|
||||||
# We use golangci-lint for linting.
|
|
||||||
# See: https://golangci-lint.run/
|
|
||||||
- name: lint
|
|
||||||
image: golangci/golangci-lint:v1.62.0
|
|
||||||
volumes:
|
|
||||||
- name: go-build-cache
|
|
||||||
path: /root/.cache/go-build
|
|
||||||
- name: golangci-lint-cache
|
|
||||||
path: /root/.cache/golangci-lint
|
|
||||||
- name: go-src
|
|
||||||
path: /go
|
|
||||||
commands:
|
|
||||||
- golangci-lint run
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
include:
|
|
||||||
- pull_request
|
|
||||||
|
|
||||||
- name: test
|
|
||||||
image: golang:1.23-alpine
|
|
||||||
volumes:
|
|
||||||
- name: go-build-cache
|
|
||||||
path: /root/.cache/go-build
|
|
||||||
- name: go-src
|
|
||||||
path: /go
|
|
||||||
- name: wazero-compilation-cache
|
|
||||||
path: /root/.cache/wazero
|
|
||||||
environment:
|
|
||||||
CGO_ENABLED: "0"
|
|
||||||
GTS_WAZERO_COMPILATION_CACHE: "/root/.cache/wazero"
|
|
||||||
commands:
|
|
||||||
- apk update --no-cache && apk add git
|
|
||||||
- >-
|
|
||||||
go test
|
|
||||||
-failfast
|
|
||||||
-timeout=20m
|
|
||||||
-tags "netgo osusergo static_build kvformat timetzdata"
|
|
||||||
./...
|
|
||||||
- ./test/envparsing.sh
|
|
||||||
- ./test/swagger.sh
|
|
||||||
depends_on:
|
|
||||||
- lint
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
include:
|
|
||||||
- pull_request
|
|
||||||
|
|
||||||
- name: web-setup
|
|
||||||
image: node:18-alpine
|
|
||||||
volumes:
|
|
||||||
- name: yarn_cache
|
|
||||||
path: /tmp/cache
|
|
||||||
commands:
|
|
||||||
- yarn --cwd ./web/source install --frozen-lockfile --cache-folder /tmp/cache
|
|
||||||
- yarn --cwd ./web/source ts-patch install # https://typia.io/docs/setup/#manual-setup
|
|
||||||
depends_on:
|
|
||||||
- test
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
include:
|
|
||||||
- pull_request
|
|
||||||
|
|
||||||
- name: web-lint
|
|
||||||
image: node:18-alpine
|
|
||||||
commands:
|
|
||||||
- yarn --cwd ./web/source lint
|
|
||||||
depends_on:
|
|
||||||
- web-setup
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
include:
|
|
||||||
- pull_request
|
|
||||||
|
|
||||||
- name: web-build
|
|
||||||
image: node:18-alpine
|
|
||||||
commands:
|
|
||||||
- yarn --cwd ./web/source build
|
|
||||||
depends_on:
|
|
||||||
- web-setup
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
include:
|
|
||||||
- pull_request
|
|
||||||
|
|
||||||
- name: snapshot
|
|
||||||
image: superseriousbusiness/gotosocial-drone-build:0.8.0 # https://github.com/superseriousbusiness/gotosocial-drone-build
|
|
||||||
volumes:
|
|
||||||
- name: go-build-cache
|
|
||||||
path: /root/.cache/go-build
|
|
||||||
- name: docker
|
|
||||||
path: /var/run/docker.sock
|
|
||||||
environment:
|
|
||||||
DOCKER_USERNAME: gotosocial
|
|
||||||
DOCKER_PASSWORD:
|
|
||||||
from_secret: gts_docker_password
|
|
||||||
S3_ACCESS_KEY_ID:
|
|
||||||
from_secret: gts_s3_access_key_id
|
|
||||||
S3_SECRET_ACCESS_KEY:
|
|
||||||
from_secret: gts_s3_secret_access_key
|
|
||||||
S3_HOSTNAME: "https://s3.superseriousbusiness.org"
|
|
||||||
S3_BUCKET_NAME: "gotosocial-snapshots"
|
|
||||||
commands:
|
|
||||||
# Create a snapshot build with GoReleaser.
|
|
||||||
- git fetch --tags
|
|
||||||
- goreleaser release --clean --snapshot
|
|
||||||
|
|
||||||
# Login to Docker, push Docker image snapshots + manifests.
|
|
||||||
- /go/dockerlogin.sh
|
|
||||||
- docker push superseriousbusiness/gotosocial:snapshot-arm64v8
|
|
||||||
- docker push superseriousbusiness/gotosocial:snapshot-amd64
|
|
||||||
- |
|
|
||||||
docker manifest create superseriousbusiness/gotosocial:snapshot \
|
|
||||||
superseriousbusiness/gotosocial:snapshot-amd64 \
|
|
||||||
superseriousbusiness/gotosocial:snapshot-arm64v8
|
|
||||||
- docker manifest push superseriousbusiness/gotosocial:snapshot
|
|
||||||
|
|
||||||
# Publish binary .tar.gz snapshots to S3.
|
|
||||||
- /go/snapshot_publish.sh
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
include:
|
|
||||||
- push
|
|
||||||
branch:
|
|
||||||
include:
|
|
||||||
- main
|
|
||||||
|
|
||||||
- name: release
|
|
||||||
image: superseriousbusiness/gotosocial-drone-build:0.8.0 # https://github.com/superseriousbusiness/gotosocial-drone-build
|
|
||||||
volumes:
|
|
||||||
- name: go-build-cache
|
|
||||||
path: /root/.cache/go-build
|
|
||||||
- name: docker
|
|
||||||
path: /var/run/docker.sock
|
|
||||||
environment:
|
|
||||||
DOCKER_USERNAME: gotosocial
|
|
||||||
DOCKER_PASSWORD:
|
|
||||||
from_secret: gts_docker_password
|
|
||||||
GITHUB_TOKEN:
|
|
||||||
from_secret: github_token
|
|
||||||
commands:
|
|
||||||
- git fetch --tags
|
|
||||||
- /go/dockerlogin.sh
|
|
||||||
|
|
||||||
# When releasing, compare commits to the most recent tag that is not the
|
|
||||||
# current one AND is not a release candidate tag (ie., no "rc" in the name).
|
|
||||||
#
|
|
||||||
# The DRONE_TAG env var should point to the tag that triggered this build.
|
|
||||||
# See: https://docs.drone.io/pipeline/environment/reference/drone-tag/
|
|
||||||
#
|
|
||||||
# Note, this may cause annoyances when doing backport releases, for example,
|
|
||||||
# releasing v0.10.1 when we've already released v0.15.0 or whatever, but
|
|
||||||
# they should only be superficial annoyances related to the release notes.
|
|
||||||
- GORELEASER_PREVIOUS_TAG=$(git tag -l | grep -v "rc\|${DRONE_TAG}" | sort -V -r | head -n 1) goreleaser release --clean
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
include:
|
|
||||||
- tag
|
|
||||||
|
|
||||||
# We can speed up builds significantly by caching build artifacts between runs.
|
|
||||||
# See: https://docs.drone.io/pipeline/docker/syntax/volumes/host/
|
|
||||||
volumes:
|
|
||||||
- name: go-build-cache
|
|
||||||
host:
|
|
||||||
path: /drone/gotosocial/go-build
|
|
||||||
- name: golangci-lint-cache
|
|
||||||
host:
|
|
||||||
path: /drone/gotosocial/golangci-lint
|
|
||||||
- name: go-src
|
|
||||||
host:
|
|
||||||
path: /drone/gotosocial/go
|
|
||||||
- name: docker
|
|
||||||
host:
|
|
||||||
path: /var/run/docker.sock
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
repo:
|
|
||||||
- superseriousbusiness/gotosocial
|
|
||||||
- NyaaaWhatsUpDoc/gotosocial
|
|
||||||
- f0x52/gotosocial
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
type: docker
|
|
||||||
name: cron
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
event:
|
|
||||||
- cron
|
|
||||||
cron:
|
|
||||||
- nightly
|
|
||||||
|
|
||||||
clone:
|
|
||||||
disable: true
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: mirror
|
|
||||||
image: superseriousbusiness/gotosocial-drone-build:0.8.0
|
|
||||||
environment:
|
|
||||||
ORIGIN_REPO: https://github.com/superseriousbusiness/gotosocial
|
|
||||||
TARGET_REPO: https://codeberg.org/superseriousbusiness/gotosocial
|
|
||||||
CODEBERG_USER: gotosocialbot
|
|
||||||
CODEBERG_EMAIL: admin@gotosocial.org
|
|
||||||
CODEBERG_TOKEN:
|
|
||||||
from_secret: gts_codeberg_token
|
|
||||||
commands:
|
|
||||||
- /go/codeberg_clone.sh
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: signature
|
|
||||||
hmac: 64ce0d466c7a48b6aa24a8836cfad7eae71faeae0b2e5342beb6428233a65eee
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
@ -73,4 +73,4 @@ body:
|
||||||
attributes:
|
attributes:
|
||||||
label: Anything else we need to know?
|
label: Anything else we need to know?
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
1
.gitea/issue_template/config.yaml
Normal file
1
.gitea/issue_template/config.yaml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
blank_issues_enabled: false
|
||||||
|
|
@ -41,4 +41,4 @@ body:
|
||||||
label: Additional context.
|
label: Additional context.
|
||||||
description: Add any other context or screenshots about the feature request here.
|
description: Add any other context or screenshots about the feature request here.
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
|
|
@ -64,4 +64,4 @@ body:
|
||||||
attributes:
|
attributes:
|
||||||
label: Anything else we need to know?
|
label: Anything else we need to know?
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
6
.gitea/issue_template/other.md
Normal file
6
.gitea/issue_template/other.md
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
name: Other
|
||||||
|
about: A different type of issue or question.
|
||||||
|
labels:
|
||||||
|
- question
|
||||||
|
---
|
||||||
|
|
@ -15,7 +15,7 @@ Please put an x inside each checkbox to indicate that you've read and followed i
|
||||||
|
|
||||||
If this is a documentation change, only the first checkbox must be filled (you can delete the others if you want).
|
If this is a documentation change, only the first checkbox must be filled (you can delete the others if you want).
|
||||||
|
|
||||||
- [ ] I/we have read the [GoToSocial contribution guidelines](https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md).
|
- [ ] I/we have read the [GoToSocial contribution guidelines](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/CONTRIBUTING.md).
|
||||||
- [ ] I/we have discussed the proposed changes already, either in an issue on the repository, or in the Matrix chat.
|
- [ ] I/we have discussed the proposed changes already, either in an issue on the repository, or in the Matrix chat.
|
||||||
- [ ] I/we have not leveraged AI to create the proposed changes.
|
- [ ] I/we have not leveraged AI to create the proposed changes.
|
||||||
- [ ] I/we have performed a self-review of added code.
|
- [ ] I/we have performed a self-review of added code.
|
||||||
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
|
|
@ -1,2 +0,0 @@
|
||||||
open_collective: gotosocial
|
|
||||||
liberapay: GoToSocial
|
|
||||||
8
.github/ISSUE_TEMPLATE/custom.md
vendored
8
.github/ISSUE_TEMPLATE/custom.md
vendored
|
|
@ -1,8 +0,0 @@
|
||||||
---
|
|
||||||
name: Custom issue template
|
|
||||||
about: Describe this issue template's purpose here.
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
5
.github/README.md
vendored
Normal file
5
.github/README.md
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# GoToSocial
|
||||||
|
|
||||||
|
This is a mirror. You can find us on https://codeberg.org/superseriousbusiness/gotosocial.
|
||||||
|
|
||||||
|
We will **stop mirroring** to Github after 0.20 is released. This will likely be sometime **in August**.
|
||||||
15
.github/dependabot.yml
vendored
15
.github/dependabot.yml
vendored
|
|
@ -1,15 +0,0 @@
|
||||||
# To get started with Dependabot version updates, you'll need to specify which
|
|
||||||
# package ecosystems to update and where the package manifests are located.
|
|
||||||
# Please see the documentation for all configuration options:
|
|
||||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
|
||||||
|
|
||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: "gomod" # See documentation for possible values
|
|
||||||
directory: "/" # Location of package manifests
|
|
||||||
schedule:
|
|
||||||
interval: "weekly"
|
|
||||||
commit-message:
|
|
||||||
prefix: "[chore]"
|
|
||||||
labels:
|
|
||||||
- "chore"
|
|
||||||
30
.github/workflows/autoclose.yaml
vendored
Normal file
30
.github/workflows/autoclose.yaml
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [opened]
|
||||||
|
pull_request_target:
|
||||||
|
types: [opened]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
autoclose:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: issue
|
||||||
|
if: ${{ github.event.issue.id != '' }}
|
||||||
|
run: |
|
||||||
|
gh issue close $ISSUE --comment "This repository is a mirror. Please open issues on https://codeberg.org/superseriousbusiness/gotosocial." --reason "not planned"
|
||||||
|
gh issue lock $ISSUE
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
ISSUE: ${{ github.event.issue.html_url }}
|
||||||
|
- name: pr
|
||||||
|
if: ${{ github.event.pull_request.id != '' }}
|
||||||
|
run: |
|
||||||
|
gh pr close $PULL_REQUEST --comment "This repository is a mirror. Please open PRs on https://codeberg.org/superseriousbusiness/gotosocial."
|
||||||
|
gh pr lock $PULL_REQUEST
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
PULL_REQUEST: ${{ github.event.pull_request.html_url }}
|
||||||
7
.gitignore
vendored
7
.gitignore
vendored
|
|
@ -4,6 +4,10 @@
|
||||||
# exclude built documentation, since readthedocs will build it for us anyway
|
# exclude built documentation, since readthedocs will build it for us anyway
|
||||||
/docs/_build
|
/docs/_build
|
||||||
|
|
||||||
|
# exclude kim's commonly used
|
||||||
|
# test stdout file location
|
||||||
|
test.out
|
||||||
|
|
||||||
# exclude coverage report
|
# exclude coverage report
|
||||||
cp.out
|
cp.out
|
||||||
|
|
||||||
|
|
@ -19,6 +23,9 @@ dist/
|
||||||
# exclude the copy of swagger.yaml moved into assets during packaging
|
# exclude the copy of swagger.yaml moved into assets during packaging
|
||||||
web/assets/swagger.yaml
|
web/assets/swagger.yaml
|
||||||
|
|
||||||
|
# exclude the copy of all_licenses.txt moved into assets during packaging
|
||||||
|
web/assets/all_licenses.txt
|
||||||
|
|
||||||
# exludes docker-volume from exemple/docker-compose
|
# exludes docker-volume from exemple/docker-compose
|
||||||
example/docker-compose/docker-volume
|
example/docker-compose/docker-volume
|
||||||
|
|
||||||
|
|
|
||||||
151
.golangci.yml
151
.golangci.yml
|
|
@ -4,91 +4,98 @@
|
||||||
#
|
#
|
||||||
# For GoToSocial we mostly take the default linters, but we add a few to catch style issues as well.
|
# For GoToSocial we mostly take the default linters, but we add a few to catch style issues as well.
|
||||||
|
|
||||||
|
version: "2"
|
||||||
# options for analysis running
|
# options for analysis running
|
||||||
run:
|
run:
|
||||||
# include test files or not, default is true
|
# include test files or not, default is true
|
||||||
tests: false
|
tests: false
|
||||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
|
||||||
timeout: 5m
|
|
||||||
|
|
||||||
linters:
|
linters:
|
||||||
# enable some extra linters, see here for the list: https://golangci-lint.run/usage/linters/
|
# enable some extra linters, see here for the list: https://golangci-lint.run/usage/linters/
|
||||||
enable:
|
enable:
|
||||||
- gocritic
|
- gocritic
|
||||||
- gofmt
|
|
||||||
- goheader
|
- goheader
|
||||||
- gosec
|
- gosec
|
||||||
- nilerr
|
- nilerr
|
||||||
- revive
|
- revive
|
||||||
|
# https://golangci-lint.run/usage/linters/#linters-configuration
|
||||||
|
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
|
||||||
|
|
||||||
# https://golangci-lint.run/usage/linters/#linters-configuration
|
This program is free software: you can redistribute it and/or modify
|
||||||
linters-settings:
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
# https://golangci-lint.run/usage/linters/#goheader
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
goheader:
|
(at your option) any later version.
|
||||||
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,
|
This program is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU Affero General Public License for more details.
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
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/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
# https://golangci-lint.run/usage/linters/#govet
|
# https://golangci-lint.run/usage/linters/#govet
|
||||||
govet:
|
govet:
|
||||||
disable:
|
disable:
|
||||||
- composites
|
- composites
|
||||||
# https://golangci-lint.run/usage/linters/#revive
|
# https://golangci-lint.run/usage/linters/#revive
|
||||||
revive:
|
revive:
|
||||||
rules:
|
rules:
|
||||||
# Enable most default rules.
|
# Enable most default rules.
|
||||||
# See: https://github.com/mgechev/revive/blob/master/defaults.toml
|
# See: https://github.com/mgechev/revive/blob/master/defaults.toml
|
||||||
- name: blank-imports
|
- name: blank-imports
|
||||||
- name: context-as-argument
|
- name: context-as-argument
|
||||||
- name: context-keys-type
|
- name: context-keys-type
|
||||||
- name: dot-imports
|
- name: dot-imports
|
||||||
- name: error-naming
|
- name: error-naming
|
||||||
- name: error-return
|
- name: error-return
|
||||||
- name: error-strings
|
- name: error-strings
|
||||||
- name: exported
|
- name: exported
|
||||||
- name: if-return
|
- name: if-return
|
||||||
- name: increment-decrement
|
- name: increment-decrement
|
||||||
- name: var-naming
|
- name: var-naming
|
||||||
- name: var-declaration
|
- name: var-declaration
|
||||||
- name: package-comments
|
- name: package-comments
|
||||||
- name: range
|
- name: range
|
||||||
- name: receiver-naming
|
- name: receiver-naming
|
||||||
- name: time-naming
|
- name: time-naming
|
||||||
- name: unexported-return
|
- name: unexported-return
|
||||||
- name: indent-error-flow
|
- name: indent-error-flow
|
||||||
- name: errorf
|
- name: errorf
|
||||||
- name: empty-block
|
- name: empty-block
|
||||||
- name: superfluous-else
|
- name: superfluous-else
|
||||||
- name: unreachable-code
|
- name: unreachable-code
|
||||||
# Disable below rules.
|
# Disable below rules.
|
||||||
- name: redefines-builtin-id
|
- name: redefines-builtin-id
|
||||||
disabled: true # This one is just annoying.
|
disabled: true
|
||||||
- name: unused-parameter
|
- name: unused-parameter
|
||||||
disabled: true # We often pass parameters to fulfil interfaces.
|
disabled: true
|
||||||
# https://golangci-lint.run/usage/linters/#staticcheck
|
- name: var-naming
|
||||||
staticcheck:
|
arguments:
|
||||||
# Enable all checks, but disable SA1012: nil context passing.
|
- []
|
||||||
# See: https://staticcheck.io/docs/configuration/options/#checks
|
- []
|
||||||
checks: ["all", "-SA1012"]
|
- - skip-package-name-checks: true
|
||||||
|
|
||||||
issues:
|
# https://golangci-lint.run/usage/linters/#staticcheck
|
||||||
exclude-rules:
|
staticcheck:
|
||||||
# Exclude VSCode custom folding region comments in files that use them.
|
# Enable all checks, but disable SA1012: nil context passing.
|
||||||
# Already fixed in go-critic and can be removed next time go-critic is updated.
|
# See: https://staticcheck.io/docs/configuration/options/#checks
|
||||||
- linters:
|
checks:
|
||||||
- gocritic
|
- SA*
|
||||||
path: internal/db/filter.go
|
- -SA1012
|
||||||
text: 'commentFormatting: put a space between `//` and comment text'
|
exclusions:
|
||||||
|
generated: lax
|
||||||
|
presets:
|
||||||
|
- comments
|
||||||
|
- common-false-positives
|
||||||
|
- legacy
|
||||||
|
- std-error-handling
|
||||||
|
formatters:
|
||||||
|
enable:
|
||||||
|
- gofmt
|
||||||
|
exclusions:
|
||||||
|
generated: lax
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,11 @@
|
||||||
version: 2
|
version: 2
|
||||||
project_name: gotosocial
|
project_name: gotosocial
|
||||||
|
|
||||||
|
# https://goreleaser.com/scm/gitea/#urls
|
||||||
|
gitea_urls:
|
||||||
|
api: https://codeberg.org/api/v1
|
||||||
|
download: https://codeberg.org
|
||||||
|
|
||||||
# https://goreleaser.com/customization/hooks/
|
# https://goreleaser.com/customization/hooks/
|
||||||
before:
|
before:
|
||||||
hooks:
|
hooks:
|
||||||
|
|
@ -12,6 +17,8 @@ before:
|
||||||
- yarn --cwd ./web/source install
|
- yarn --cwd ./web/source install
|
||||||
- yarn --cwd ./web/source ts-patch install # https://typia.io/docs/setup/#manual-setup
|
- yarn --cwd ./web/source ts-patch install # https://typia.io/docs/setup/#manual-setup
|
||||||
- yarn --cwd ./web/source build
|
- yarn --cwd ./web/source build
|
||||||
|
# Bundle all licenses into web/assets/all_licenses.txt
|
||||||
|
- ./scripts/bundle_licenses.sh
|
||||||
|
|
||||||
# https://goreleaser.com/customization/build/
|
# https://goreleaser.com/customization/build/
|
||||||
builds:
|
builds:
|
||||||
|
|
@ -102,15 +109,21 @@ dockers:
|
||||||
ids:
|
ids:
|
||||||
- gotosocial
|
- gotosocial
|
||||||
image_templates:
|
image_templates:
|
||||||
- "superseriousbusiness/{{ .ProjectName }}:{{ .Version }}-amd64"
|
- "{{ if not .IsSnapshot }}superseriousbusiness/{{ .ProjectName }}:{{ .Version }}-amd64{{ end }}" # Use version tag (eg., `0.19.0`, `0.19.0-rc1`) for proper releases and prereleases.
|
||||||
- "superseriousbusiness/{{ .ProjectName }}:latest-amd64"
|
- "{{ if and (not .Prerelease) (not .IsSnapshot) }}superseriousbusiness/{{ .ProjectName }}:latest-amd64{{ end }}" # Only use `latest` for proper releases, not prereleases or snapshots.
|
||||||
- "{{ if .IsSnapshot }}superseriousbusiness/{{ .ProjectName }}:snapshot-amd64{{ end }}"
|
- "{{ if .IsSnapshot }}superseriousbusiness/{{ .ProjectName }}:snapshot-amd64{{ end }}" # Only use `snapshot` for snapshot builds triggered by merge to main.
|
||||||
build_flag_templates:
|
build_flag_templates:
|
||||||
- "--platform=linux/amd64"
|
- "--platform=linux/amd64"
|
||||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
- "--label=org.opencontainers.image.title=GoToSocial"
|
||||||
- "--label=org.opencontainers.image.title={{.ProjectName}}"
|
- "--label=org.opencontainers.image.authors=GoToSocial Authors"
|
||||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
- "--label=org.opencontainers.image.description=Fast, fun, small ActivityPub server."
|
||||||
|
- "--label=org.opencontainers.image.url=https://docs.gotosocial.org"
|
||||||
|
- "--label=org.opencontainers.image.documentation=https://docs.gotosocial.org/en/latest/getting_started/installation/container/"
|
||||||
|
- "--label=org.opencontainers.image.source=https://codeberg.org/superseriousbusiness/gotosocial"
|
||||||
- "--label=org.opencontainers.image.version={{.Version}}"
|
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||||
|
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||||
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
|
- "--label=org.opencontainers.image.licenses=AGPL-3.0-or-later"
|
||||||
extra_files:
|
extra_files:
|
||||||
- web
|
- web
|
||||||
- go.mod
|
- go.mod
|
||||||
|
|
@ -125,15 +138,21 @@ dockers:
|
||||||
ids:
|
ids:
|
||||||
- gotosocial
|
- gotosocial
|
||||||
image_templates:
|
image_templates:
|
||||||
- "superseriousbusiness/{{ .ProjectName }}:{{ .Version }}-arm64v8"
|
- "{{ if not .IsSnapshot }}superseriousbusiness/{{ .ProjectName }}:{{ .Version }}-arm64v8{{ end }}" # Use version tag (eg., `0.19.0`, `0.19.0-rc1`) for proper releases and prereleases.
|
||||||
- "superseriousbusiness/{{ .ProjectName }}:latest-arm64v8"
|
- "{{ if and (not .Prerelease) (not .IsSnapshot) }}superseriousbusiness/{{ .ProjectName }}:latest-arm64v8{{ end }}" # Only use `latest` for proper releases, not prereleases or snapshots.
|
||||||
- "{{ if .IsSnapshot }}superseriousbusiness/{{ .ProjectName }}:snapshot-arm64v8{{ end }}"
|
- "{{ if .IsSnapshot }}superseriousbusiness/{{ .ProjectName }}:snapshot-arm64v8{{ end }}" # Only use `snapshot` for snapshot builds triggered by merge to main.
|
||||||
build_flag_templates:
|
build_flag_templates:
|
||||||
- "--platform=linux/arm64/v8"
|
- "--platform=linux/arm64/v8"
|
||||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
- "--label=org.opencontainers.image.title=GoToSocial"
|
||||||
- "--label=org.opencontainers.image.title={{.ProjectName}}"
|
- "--label=org.opencontainers.image.authors=GoToSocial Authors"
|
||||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
- "--label=org.opencontainers.image.description=Fast, fun, small ActivityPub server."
|
||||||
|
- "--label=org.opencontainers.image.url=https://docs.gotosocial.org"
|
||||||
|
- "--label=org.opencontainers.image.documentation=https://docs.gotosocial.org/en/latest/getting_started/installation/container/"
|
||||||
|
- "--label=org.opencontainers.image.source=https://codeberg.org/superseriousbusiness/gotosocial"
|
||||||
- "--label=org.opencontainers.image.version={{.Version}}"
|
- "--label=org.opencontainers.image.version={{.Version}}"
|
||||||
|
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
||||||
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
|
- "--label=org.opencontainers.image.licenses=AGPL-3.0-or-later"
|
||||||
extra_files:
|
extra_files:
|
||||||
- web
|
- web
|
||||||
- go.mod
|
- go.mod
|
||||||
|
|
@ -143,14 +162,17 @@ dockers:
|
||||||
|
|
||||||
# https://goreleaser.com/customization/docker_manifest/
|
# https://goreleaser.com/customization/docker_manifest/
|
||||||
docker_manifests:
|
docker_manifests:
|
||||||
- name_template: superseriousbusiness/{{ .ProjectName }}:{{ .Version }}
|
# Use version tag (eg., `0.19.0`, `0.19.0-rc1`) for proper releases and prereleases.
|
||||||
|
- name_template: "{{ if not .IsSnapshot }}superseriousbusiness/{{ .ProjectName }}:{{ .Version }}{{ end }}"
|
||||||
image_templates:
|
image_templates:
|
||||||
- superseriousbusiness/{{ .ProjectName }}:{{ .Version }}-amd64
|
- superseriousbusiness/{{ .ProjectName }}:{{ .Version }}-amd64
|
||||||
- superseriousbusiness/{{ .ProjectName }}:{{ .Version }}-arm64v8
|
- superseriousbusiness/{{ .ProjectName }}:{{ .Version }}-arm64v8
|
||||||
- name_template: superseriousbusiness/{{ .ProjectName }}:latest
|
# Only use `latest` for proper releases, not prereleases or snapshots.
|
||||||
|
- name_template: "{{ if and (not .Prerelease) (not .IsSnapshot) }}superseriousbusiness/{{ .ProjectName }}:latest{{ end }}"
|
||||||
image_templates:
|
image_templates:
|
||||||
- superseriousbusiness/{{ .ProjectName }}:latest-amd64
|
- superseriousbusiness/{{ .ProjectName }}:latest-amd64
|
||||||
- superseriousbusiness/{{ .ProjectName }}:latest-arm64v8
|
- superseriousbusiness/{{ .ProjectName }}:latest-arm64v8
|
||||||
|
# Only use `snapshot` for snapshot builds triggered by merge to main.
|
||||||
- name_template: "{{ if .IsSnapshot }}superseriousbusiness/{{ .ProjectName }}:snapshot{{ end }}"
|
- name_template: "{{ if .IsSnapshot }}superseriousbusiness/{{ .ProjectName }}:snapshot{{ end }}"
|
||||||
image_templates:
|
image_templates:
|
||||||
- superseriousbusiness/{{ .ProjectName }}:snapshot-amd64
|
- superseriousbusiness/{{ .ProjectName }}:snapshot-amd64
|
||||||
|
|
@ -217,6 +239,10 @@ source:
|
||||||
|
|
||||||
# https://goreleaser.com/customization/release/
|
# https://goreleaser.com/customization/release/
|
||||||
release:
|
release:
|
||||||
|
# https://goreleaser.com/customization/release/#gitea
|
||||||
|
gitea:
|
||||||
|
owner: superseriousbusiness
|
||||||
|
name: gotosocial
|
||||||
draft: true
|
draft: true
|
||||||
prerelease: auto
|
prerelease: auto
|
||||||
header: |
|
header: |
|
||||||
|
|
@ -239,8 +265,8 @@ release:
|
||||||
#### Binary/tar
|
#### Binary/tar
|
||||||
|
|
||||||
1. Stop GoToSocial.
|
1. Stop GoToSocial.
|
||||||
2. **Back up your database!** If you're running on SQLite, this is as simple as copying your `sqlite.db` file, eg., `cp sqlite.db sqlite.db.backup`.
|
2. **Back up your database!** If you're running on SQLite, this is as simple as copying your `sqlite.db` file, eg., `cp sqlite.db sqlite.db.backup`. On Postgres you can do this with [pg_dump](https://www.postgresql.org/docs/current/backup-dump.html).
|
||||||
3. Download and untar the new release, including the web assets and html templates.
|
3. Download and untar the new release, **including the web assets and html templates**, not just the binary.
|
||||||
4. Edit your config.yaml file if necessary (see below).
|
4. Edit your config.yaml file if necessary (see below).
|
||||||
5. Start GoToSocial.
|
5. Start GoToSocial.
|
||||||
6. Wait patiently for any migrations to run, **do not interrupt migrations or you could leave your db in a broken state and will have to restore from backup**!
|
6. Wait patiently for any migrations to run, **do not interrupt migrations or you could leave your db in a broken state and will have to restore from backup**!
|
||||||
|
|
@ -249,8 +275,8 @@ release:
|
||||||
#### Docker
|
#### Docker
|
||||||
|
|
||||||
1. Stop GoToSocial.
|
1. Stop GoToSocial.
|
||||||
2. **Back up your database!** If you're running on SQLite, this is as simple as copying your `sqlite.db` file, eg., `cp sqlite.db sqlite.db.backup`.
|
2. **Back up your database!** If you're running on SQLite, this is as simple as copying your `sqlite.db` file, eg., `cp sqlite.db sqlite.db.backup`. On Postgres you can do this with [pg_dump](https://www.postgresql.org/docs/current/backup-dump.html).
|
||||||
3. Pull the new docker container (`superseriousbusiness/gotosocial:{{ .Version }}` or `superseriousbusiness/gotosocial:latest`)
|
3. Pull the new docker container with `docker pull docker.io/superseriousbusiness/gotosocial:{{ .Version }}` or `docker pull docker.io/superseriousbusiness/gotosocial:latest` if this is a stable release and not a release candidate.
|
||||||
4. Edit your config.yaml file or environment variables if necessary (see below).
|
4. Edit your config.yaml file or environment variables if necessary (see below).
|
||||||
5. Start GoToSocial.
|
5. Start GoToSocial.
|
||||||
6. Wait patiently for any migrations to run, **do not interrupt migrations or you could leave your db in a broken state and will have to restore from backup**!
|
6. Wait patiently for any migrations to run, **do not interrupt migrations or you could leave your db in a broken state and will have to restore from backup**!
|
||||||
|
|
@ -263,7 +289,7 @@ release:
|
||||||
- Changed `pee pee` to `poo poo`.
|
- Changed `pee pee` to `poo poo`.
|
||||||
- Changed `wee wee` to `more wee wee`.
|
- Changed `wee wee` to `more wee wee`.
|
||||||
|
|
||||||
You can see a diff of the config file here: https://github.com/superseriousbusiness/gotosocial/compare/{{ .PreviousTag }}...{{ .Tag }}#diff-c071e03510b2c57e193a44503fd9528a785f0f411497cc75841a9f8d0b1ac622
|
You can see a diff of the config file here: https://codeberg.org/superseriousbusiness/gotosocial/compare/{{ .PreviousTag }}...{{ .Tag }}#diff-c071e03510b2c57e193a44503fd9528a785f0f411497cc75841a9f8d0b1ac622
|
||||||
|
|
||||||
### Database Migrations
|
### Database Migrations
|
||||||
|
|
||||||
|
|
@ -283,14 +309,14 @@ release:
|
||||||
|
|
||||||
GoToSocial releases binary builds for 64-bit Linux, FreeBSD, and NetBSD operating systems. We also release Docker builds for 64-bit Linux.
|
GoToSocial releases binary builds for 64-bit Linux, FreeBSD, and NetBSD operating systems. We also release Docker builds for 64-bit Linux.
|
||||||
|
|
||||||
| OS | Architecture | Support level | Binary archive | Docker |
|
| OS | Architecture | Support level | Binary archive | Docker |
|
||||||
| ------- | ----------------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
|
| ------- | ----------------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
|
||||||
| Linux | x86-64/AMD64 (64-bit) | 🟢 Full | [linux_amd64.tar.gz](https://github.com/superseriousbusiness/gotosocial/releases/download/{{ .Tag }}/gotosocial_{{ .Version }}_linux_amd64.tar.gz) | `superseriousbusiness/gotosocial:{{ .Version }}` |
|
| Linux | x86-64/AMD64 (64-bit) | 🟢 Full | [linux_amd64.tar.gz](https://codeberg.org/superseriousbusiness/gotosocial/releases/download/{{ .Tag }}/gotosocial_{{ .Version }}_linux_amd64.tar.gz) | `docker.io/superseriousbusiness/gotosocial:{{ .Version }}` |
|
||||||
| Linux | Armv8/ARM64 (64-bit) | 🟢 Full | [linux_arm64.tar.gz](https://github.com/superseriousbusiness/gotosocial/releases/download/{{ .Tag }}/gotosocial_{{ .Version }}_linux_arm64.tar.gz) | `superseriousbusiness/gotosocial:{{ .Version }}` |
|
| Linux | Armv8/ARM64 (64-bit) | 🟢 Full | [linux_arm64.tar.gz](https://codeberg.org/superseriousbusiness/gotosocial/releases/download/{{ .Tag }}/gotosocial_{{ .Version }}_linux_arm64.tar.gz) | `docker.io/superseriousbusiness/gotosocial:{{ .Version }}` |
|
||||||
| FreeBSD | x86-64/AMD64 (64-bit) | 🟢 Full | [freebsd_amd64.tar.gz](https://github.com/superseriousbusiness/gotosocial/releases/download/{{ .Tag }}/gotosocial_{{ .Version }}_freebsd_amd64.tar.gz) | Not provided |
|
| FreeBSD | x86-64/AMD64 (64-bit) | 🟢 Full | [freebsd_amd64.tar.gz](https://codeberg.org/superseriousbusiness/gotosocial/releases/download/{{ .Tag }}/gotosocial_{{ .Version }}_freebsd_amd64.tar.gz) | Not provided |
|
||||||
| FreeBSD | Armv8/ARM64 (64-bit) | 🟢 Full | [freebsd_arm64.tar.gz](https://github.com/superseriousbusiness/gotosocial/releases/download/{{ .Tag }}/gotosocial_{{ .Version }}_freebsd_arm64.tar.gz) | Not provided |
|
| FreeBSD | Armv8/ARM64 (64-bit) | 🟢 Full | [freebsd_arm64.tar.gz](https://codeberg.org/superseriousbusiness/gotosocial/releases/download/{{ .Tag }}/gotosocial_{{ .Version }}_freebsd_arm64.tar.gz) | Not provided |
|
||||||
| NetBSD | x86-64/AMD64 (64-bit) | 🟢 Full | [netbsd_amd64.tar.gz](https://github.com/superseriousbusiness/gotosocial/releases/download/{{ .Tag }}/gotosocial_{{ .Version }}_netbsd_amd64.tar.gz) | Not provided |
|
| NetBSD | x86-64/AMD64 (64-bit) | 🟢 Full | [netbsd_amd64.tar.gz](https://codeberg.org/superseriousbusiness/gotosocial/releases/download/{{ .Tag }}/gotosocial_{{ .Version }}_netbsd_amd64.tar.gz) | Not provided |
|
||||||
| NetBSD | Armv8/ARM64 (64-bit) | 🟢 Full | [netbsd_arm64.tar.gz](https://github.com/superseriousbusiness/gotosocial/releases/download/{{ .Tag }}/gotosocial_{{ .Version }}_netbsd_arm64.tar.gz) | Not provided |
|
| NetBSD | Armv8/ARM64 (64-bit) | 🟢 Full | [netbsd_arm64.tar.gz](https://codeberg.org/superseriousbusiness/gotosocial/releases/download/{{ .Tag }}/gotosocial_{{ .Version }}_netbsd_arm64.tar.gz) | Not provided |
|
||||||
|
|
||||||
#### `nowasm`
|
#### `nowasm`
|
||||||
|
|
||||||
|
|
@ -304,7 +330,7 @@ release:
|
||||||
|
|
||||||
# https://goreleaser.com/customization/changelog/
|
# https://goreleaser.com/customization/changelog/
|
||||||
changelog:
|
changelog:
|
||||||
use: github
|
use: gitea
|
||||||
groups:
|
groups:
|
||||||
- title: Features and performance
|
- title: Features and performance
|
||||||
regexp: '^.*\[(?:feature|performance).*\].*$'
|
regexp: '^.*\[(?:feature|performance).*\].*$'
|
||||||
|
|
|
||||||
137
.woodpecker/pr.yaml
Normal file
137
.woodpecker/pr.yaml
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
# https://woodpecker-ci.org/docs/usage/workflow-syntax#when---global-workflow-conditions
|
||||||
|
when:
|
||||||
|
- event: pull_request
|
||||||
|
|
||||||
|
steps:
|
||||||
|
# Lint the Go code only if
|
||||||
|
# some Go files have changed.
|
||||||
|
#
|
||||||
|
# CI_PIPELINE_FILES is undefined if
|
||||||
|
# files changed > 500, and empty on
|
||||||
|
# force pushes, so account for this
|
||||||
|
# and run step to be safe.
|
||||||
|
lint:
|
||||||
|
when:
|
||||||
|
# https://woodpecker-ci.org/docs/usage/workflow-syntax#evaluate
|
||||||
|
# https://woodpecker-ci.org/docs/usage/environment#built-in-environment-variables
|
||||||
|
- evaluate: >-
|
||||||
|
(not ("CI_PIPELINE_FILES" in $env)) ||
|
||||||
|
CI_PIPELINE_FILES == "[]" ||
|
||||||
|
any(fromJSON(CI_PIPELINE_FILES), { # startsWith "internal/" || # startsWith "cmd/" || # startsWith "testrig/" }) ||
|
||||||
|
len(fromJSON(CI_PIPELINE_FILES)) == 0
|
||||||
|
|
||||||
|
# We use golangci-lint for linting.
|
||||||
|
# See: https://golangci-lint.run/
|
||||||
|
image: golangci/golangci-lint:v2.3.1
|
||||||
|
pull: true
|
||||||
|
|
||||||
|
# https://woodpecker-ci.org/docs/administration/configuration/backends/docker#run-user
|
||||||
|
backend_options:
|
||||||
|
docker:
|
||||||
|
user: 1000:1000
|
||||||
|
|
||||||
|
# https://woodpecker-ci.org/docs/usage/volumes
|
||||||
|
volumes:
|
||||||
|
- /woodpecker/gotosocial/go-build-cache:/.cache/go-build
|
||||||
|
- /woodpecker/gotosocial/go-pkg-cache:/go/pkg
|
||||||
|
- /woodpecker/gotosocial/golangci-lint-cache:/.cache/golangci-lint
|
||||||
|
|
||||||
|
# https://woodpecker-ci.org/docs/usage/environment
|
||||||
|
environment:
|
||||||
|
GOFLAGS: "-buildvcs=false"
|
||||||
|
|
||||||
|
# https://woodpecker-ci.org/docs/usage/workflow-syntax#commands
|
||||||
|
commands:
|
||||||
|
- golangci-lint run
|
||||||
|
|
||||||
|
# Test the Go code only if
|
||||||
|
# some Go files have changed.
|
||||||
|
#
|
||||||
|
# CI_PIPELINE_FILES is undefined if
|
||||||
|
# files changed > 500, and empty on
|
||||||
|
# force pushes, so account for this
|
||||||
|
# and run step to be safe.
|
||||||
|
test:
|
||||||
|
when:
|
||||||
|
# https://woodpecker-ci.org/docs/usage/workflow-syntax#evaluate
|
||||||
|
# https://woodpecker-ci.org/docs/usage/environment#built-in-environment-variables
|
||||||
|
- evaluate: >-
|
||||||
|
(not ("CI_PIPELINE_FILES" in $env)) ||
|
||||||
|
CI_PIPELINE_FILES == "[]" ||
|
||||||
|
any(fromJSON(CI_PIPELINE_FILES), { # startsWith "internal/" || # startsWith "cmd/" || # startsWith "testrig/" || # startsWith "vendor/" }) ||
|
||||||
|
len(fromJSON(CI_PIPELINE_FILES)) == 0
|
||||||
|
|
||||||
|
image: golang:1.24-alpine
|
||||||
|
pull: true
|
||||||
|
|
||||||
|
# https://woodpecker-ci.org/docs/administration/configuration/backends/docker#run-user
|
||||||
|
backend_options:
|
||||||
|
docker:
|
||||||
|
user: 1000:1000
|
||||||
|
|
||||||
|
# https://woodpecker-ci.org/docs/usage/volumes
|
||||||
|
volumes:
|
||||||
|
- /woodpecker/gotosocial/go-build-cache:/.cache/go-build
|
||||||
|
- /woodpecker/gotosocial/go-pkg-cache:/go/pkg
|
||||||
|
- /woodpecker/gotosocial/wazero-compilation-cache:/.cache/wazero
|
||||||
|
- /woodpecker/gotosocial/test-tmp:/tmp
|
||||||
|
|
||||||
|
# https://woodpecker-ci.org/docs/usage/environment
|
||||||
|
environment:
|
||||||
|
CGO_ENABLED: "0"
|
||||||
|
GTS_WAZERO_COMPILATION_CACHE: "/.cache/wazero"
|
||||||
|
|
||||||
|
# https://woodpecker-ci.org/docs/usage/workflow-syntax#commands
|
||||||
|
commands:
|
||||||
|
- >-
|
||||||
|
go test
|
||||||
|
-ldflags="-s -w -extldflags '-static'"
|
||||||
|
-tags="netgo osusergo static_build kvformat timetzdata"
|
||||||
|
-failfast
|
||||||
|
-timeout=30m
|
||||||
|
./...
|
||||||
|
- ./test/envparsing.sh
|
||||||
|
- ./test/swagger.sh
|
||||||
|
|
||||||
|
# Validate the web code only
|
||||||
|
# if web source has changed.
|
||||||
|
#
|
||||||
|
# CI_PIPELINE_FILES is undefined if
|
||||||
|
# files changed > 500, and empty on
|
||||||
|
# force pushes, so account for this
|
||||||
|
# and run step to be safe.
|
||||||
|
web:
|
||||||
|
when:
|
||||||
|
# https://woodpecker-ci.org/docs/usage/workflow-syntax#evaluate
|
||||||
|
# https://woodpecker-ci.org/docs/usage/environment#built-in-environment-variables
|
||||||
|
- evaluate: >-
|
||||||
|
(not ("CI_PIPELINE_FILES" in $env)) ||
|
||||||
|
CI_PIPELINE_FILES == "[]" ||
|
||||||
|
any(fromJSON(CI_PIPELINE_FILES), { # startsWith "web/source/" }) ||
|
||||||
|
len(fromJSON(CI_PIPELINE_FILES)) == 0
|
||||||
|
|
||||||
|
image: node:lts-alpine
|
||||||
|
pull: true
|
||||||
|
|
||||||
|
# https://woodpecker-ci.org/docs/administration/configuration/backends/docker#run-user
|
||||||
|
backend_options:
|
||||||
|
docker:
|
||||||
|
user: 1000:1000
|
||||||
|
|
||||||
|
# https://woodpecker-ci.org/docs/usage/volumes
|
||||||
|
volumes:
|
||||||
|
- /woodpecker/gotosocial/node_modules:/woodpecker/src/codeberg.org/superseriousbusiness/gotosocial/web/source/node_modules
|
||||||
|
- /woodpecker/gotosocial/yarn-cache:/.cache/yarn
|
||||||
|
- /woodpecker/gotosocial/web-dist-test:/woodpecker/src/codeberg.org/superseriousbusiness/gotosocial/web/assets/dist
|
||||||
|
|
||||||
|
# https://woodpecker-ci.org/docs/usage/workflow-syntax#commands
|
||||||
|
commands:
|
||||||
|
# Install web dependencies.
|
||||||
|
- yarn --cwd ./web/source install --frozen-lockfile --cache-folder /.cache/yarn
|
||||||
|
- yarn --cwd ./web/source ts-patch install # https://typia.io/docs/setup/#manual-setup
|
||||||
|
|
||||||
|
# Lint web source.
|
||||||
|
- yarn --cwd ./web/source lint
|
||||||
|
|
||||||
|
# Ensure build works.
|
||||||
|
- yarn --cwd ./web/source build
|
||||||
54
.woodpecker/release.yaml
Normal file
54
.woodpecker/release.yaml
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
# https://woodpecker-ci.org/docs/usage/workflow-syntax#when---global-workflow-conditions
|
||||||
|
when:
|
||||||
|
- event: tag
|
||||||
|
|
||||||
|
# https://goreleaser.com/ci/woodpecker/
|
||||||
|
# https://woodpecker-ci.org/docs/usage/workflow-syntax#clone
|
||||||
|
clone:
|
||||||
|
git:
|
||||||
|
image: woodpeckerci/plugin-git
|
||||||
|
settings:
|
||||||
|
tags: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
release:
|
||||||
|
# https://codeberg.org/superseriousbusiness/gotosocial-woodpecker-build
|
||||||
|
image: superseriousbusiness/gotosocial-woodpecker-build:0.12.1
|
||||||
|
pull: true
|
||||||
|
|
||||||
|
# https://woodpecker-ci.org/docs/usage/volumes
|
||||||
|
volumes:
|
||||||
|
- /woodpecker/gotosocial/go-build-cache-root:/root/.cache/go-build
|
||||||
|
- /woodpecker/gotosocial/go-pkg-cache-root:/go/pkg
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
|
||||||
|
# https://woodpecker-ci.org/docs/usage/environment
|
||||||
|
# https://woodpecker-ci.org/docs/usage/secrets#usage
|
||||||
|
environment:
|
||||||
|
# Needed for goreleaser to
|
||||||
|
# push manifests + containers.
|
||||||
|
DOCKER_USERNAME: gotosocial
|
||||||
|
DOCKER_PASSWORD:
|
||||||
|
from_secret: gts_docker_password
|
||||||
|
|
||||||
|
# Needed for goreleaser
|
||||||
|
# to publish the release.
|
||||||
|
# https://goreleaser.com/scm/gitea/
|
||||||
|
GITEA_TOKEN:
|
||||||
|
from_secret: codeberg_token
|
||||||
|
|
||||||
|
# https://woodpecker-ci.org/docs/usage/workflow-syntax#commands
|
||||||
|
commands:
|
||||||
|
- git fetch --tags
|
||||||
|
- /go/dockerlogin.sh
|
||||||
|
|
||||||
|
# When releasing, compare commits to the most recent tag that is not the
|
||||||
|
# current one AND is not a release candidate tag (ie., no "rc" in the name).
|
||||||
|
#
|
||||||
|
# The CI_COMMIT_TAG env var should point to the tag that triggered this build.
|
||||||
|
# See: https://woodpecker-ci.org/docs/usage/environment
|
||||||
|
#
|
||||||
|
# Note, this may cause annoyances when doing backport releases, for example,
|
||||||
|
# releasing v0.10.1 when we've already released v0.15.0 or whatever, but
|
||||||
|
# they should only be superficial annoyances related to the release notes.
|
||||||
|
- GORELEASER_PREVIOUS_TAG=$(git tag -l | grep -v "rc\|${CI_COMMIT_TAG}" | sort -V -r | head -n 1) goreleaser release --clean
|
||||||
76
.woodpecker/snapshot.yaml
Normal file
76
.woodpecker/snapshot.yaml
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
# https://woodpecker-ci.org/docs/usage/workflow-syntax#when---global-workflow-conditions
|
||||||
|
when:
|
||||||
|
- event: push
|
||||||
|
branch: main
|
||||||
|
|
||||||
|
# https://goreleaser.com/ci/woodpecker/
|
||||||
|
# https://woodpecker-ci.org/docs/usage/workflow-syntax#clone
|
||||||
|
clone:
|
||||||
|
git:
|
||||||
|
image: woodpeckerci/plugin-git
|
||||||
|
settings:
|
||||||
|
tags: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
snapshot:
|
||||||
|
# Snapshot only if some interesting
|
||||||
|
# source code files have changed.
|
||||||
|
#
|
||||||
|
# CI_PIPELINE_FILES is undefined if
|
||||||
|
# files changed > 500, so account for
|
||||||
|
# this and snapshot anyway if so.
|
||||||
|
when:
|
||||||
|
# https://woodpecker-ci.org/docs/usage/workflow-syntax#evaluate
|
||||||
|
# https://woodpecker-ci.org/docs/usage/environment#built-in-environment-variables
|
||||||
|
- evaluate: >-
|
||||||
|
(not ("CI_PIPELINE_FILES" in $env)) ||
|
||||||
|
CI_PIPELINE_FILES == "[]" ||
|
||||||
|
any(fromJSON(CI_PIPELINE_FILES), { # startsWith "internal/" || # startsWith "cmd/" || # startsWith "testrig/" || # startsWith "vendor/" || # startsWith "web/" || # == "Dockerfile" }) ||
|
||||||
|
len(fromJSON(CI_PIPELINE_FILES)) == 0
|
||||||
|
|
||||||
|
# https://codeberg.org/superseriousbusiness/gotosocial-woodpecker-build
|
||||||
|
image: superseriousbusiness/gotosocial-woodpecker-build:0.12.1
|
||||||
|
pull: true
|
||||||
|
|
||||||
|
# https://woodpecker-ci.org/docs/usage/volumes
|
||||||
|
volumes:
|
||||||
|
- /woodpecker/gotosocial/go-build-cache-root:/root/.cache/go-build
|
||||||
|
- /woodpecker/gotosocial/go-pkg-cache-root:/go/pkg
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
|
||||||
|
# https://woodpecker-ci.org/docs/usage/environment
|
||||||
|
# https://woodpecker-ci.org/docs/usage/secrets#usage
|
||||||
|
environment:
|
||||||
|
# Needed to push snapshot
|
||||||
|
# manifests + containers.
|
||||||
|
DOCKER_USERNAME: gotosocial
|
||||||
|
DOCKER_PASSWORD:
|
||||||
|
from_secret: gts_docker_password
|
||||||
|
|
||||||
|
# Needed for snapshot script
|
||||||
|
# to publish artifacts to S3.
|
||||||
|
S3_ACCESS_KEY_ID:
|
||||||
|
from_secret: gts_s3_access_key_id
|
||||||
|
S3_SECRET_ACCESS_KEY:
|
||||||
|
from_secret: gts_s3_secret_access_key
|
||||||
|
S3_HOSTNAME: "https://s3.superseriousbusiness.org"
|
||||||
|
S3_BUCKET_NAME: "gotosocial-snapshots"
|
||||||
|
|
||||||
|
# https://woodpecker-ci.org/docs/usage/workflow-syntax#commands
|
||||||
|
commands:
|
||||||
|
# Create a snapshot build with GoReleaser.
|
||||||
|
- git fetch --tags
|
||||||
|
- goreleaser release --clean --snapshot
|
||||||
|
|
||||||
|
# Login to Docker, push Docker image snapshots + manifests.
|
||||||
|
- /go/dockerlogin.sh
|
||||||
|
- docker push superseriousbusiness/gotosocial:snapshot-arm64v8
|
||||||
|
- docker push superseriousbusiness/gotosocial:snapshot-amd64
|
||||||
|
- |
|
||||||
|
docker manifest create superseriousbusiness/gotosocial:snapshot \
|
||||||
|
superseriousbusiness/gotosocial:snapshot-amd64 \
|
||||||
|
superseriousbusiness/gotosocial:snapshot-arm64v8
|
||||||
|
- docker manifest push superseriousbusiness/gotosocial:snapshot
|
||||||
|
|
||||||
|
# Publish binary .tar.gz snapshots to S3.
|
||||||
|
- /go/snapshot_publish.sh
|
||||||
160
CONTRIBUTING.md
160
CONTRIBUTING.md
|
|
@ -18,22 +18,23 @@ These contribution guidelines were adapted from / inspired by those of Gitea (ht
|
||||||
- [Docker](#docker)
|
- [Docker](#docker)
|
||||||
- [With GoReleaser](#with-goreleaser)
|
- [With GoReleaser](#with-goreleaser)
|
||||||
- [Manually](#manually)
|
- [Manually](#manually)
|
||||||
- [Stylesheet / Web dev](#stylesheet--web-dev)
|
- [Stylesheet / Web dev](#stylesheet-web-dev)
|
||||||
- [Live Reloading](#live-reloading)
|
- [Live Reloading](#live-reloading)
|
||||||
- [Project Structure](#project-structure)
|
- [Project Structure](#project-structure)
|
||||||
- [Finding your way around the code](#finding-your-way-around-the-code)
|
- [Finding your way around the code](#finding-your-way-around-the-code)
|
||||||
- [Style / Linting / Formatting](#style--linting--formatting)
|
- [Style / Linting / Formatting](#style-linting-formatting)
|
||||||
- [Testing](#testing)
|
- [Testing](#testing)
|
||||||
- [Standalone Testrig with Pinafore](#standalone-testrig-with-pinafore)
|
- [Standalone Testrig with Pinafore](#standalone-testrig-with-pinafore)
|
||||||
|
- [Configuring the Standalone Testrig](#configuring-the-standalone-testrig)
|
||||||
- [Running automated tests](#running-automated-tests)
|
- [Running automated tests](#running-automated-tests)
|
||||||
- [SQLite](#sqlite)
|
- [SQLite](#sqlite)
|
||||||
- [Postgres](#postgres)
|
- [Postgres](#postgres)
|
||||||
- [CLI Tests](#cli-tests)
|
- [CLI Tests](#cli-tests)
|
||||||
- [Federation](#federation)
|
- [Federation](#federation)
|
||||||
- [Updating Swagger docs](#updating-swagger-docs)
|
- [Updating Swagger docs](#updating-swagger-docs)
|
||||||
- [CI/CD configuration](#cicd-configuration)
|
- [CI/CD configuration](#ci-cd-configuration)
|
||||||
- [Release Checklist](#release-checklist)
|
- [Other Useful Stuff](#other-useful-stuff)
|
||||||
- [What if something goes wrong?](#what-if-something-goes-wrong)
|
- [Running migrations on a Postgres DB backup locally](#running-migrations-on-a-postgres-db-backup-locally)
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
|
|
@ -41,11 +42,11 @@ This document contains important information that will help you to write a succe
|
||||||
|
|
||||||
## Bug reports and feature requests
|
## Bug reports and feature requests
|
||||||
|
|
||||||
Currently, we use Github's issue system for tracking bug reports and feature requests.
|
Currently, we use Codeberg's issue system for tracking bug reports and feature requests.
|
||||||
|
|
||||||
You can view all open issues [here](https://github.com/superseriousbusiness/gotosocial/issues "The Github Issues page for GoToSocial").
|
You can view all open issues [here](https://codeberg.org/superseriousbusiness/gotosocial/issues "The Codeberg Issues page for GoToSocial").
|
||||||
|
|
||||||
Before opening a new issue, whether bug or feature request, **please search carefully through both open and closed issues to make sure it hasn't been addressed already**. You can use Github's keyword issue search for this. If your issue is a duplicate of an existing issue, it will be closed.
|
Before opening a new issue, whether bug or feature request, **please search carefully through both open and closed issues to make sure it hasn't been addressed already**. You can use Codeberg's keyword issue search for this. If your issue is a duplicate of an existing issue, it will be closed.
|
||||||
|
|
||||||
Before you open a feature request issue, please consider the following:
|
Before you open a feature request issue, please consider the following:
|
||||||
|
|
||||||
|
|
@ -99,7 +100,7 @@ If you see something in the documentation that's missing, wrong, or unclear, fee
|
||||||
|
|
||||||
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:
|
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)
|
* Install [`miniconda`](https://www.anaconda.com/docs/getting-started/miniconda/main)
|
||||||
* Create your conda environment: `conda env create -f ./docs/environment.yml`
|
* Create your conda environment: `conda env create -f ./docs/environment.yml`
|
||||||
* Activate the environment: `conda activate gotosocial-docs`
|
* Activate the environment: `conda activate gotosocial-docs`
|
||||||
* Serve locally: `mkdocs serve`
|
* Serve locally: `mkdocs serve`
|
||||||
|
|
@ -122,26 +123,22 @@ Beware that `conda env export` will add a `prefix` entry to the environment.yml
|
||||||
|
|
||||||
### Golang forking quirks
|
### Golang forking quirks
|
||||||
|
|
||||||
One of the quirks of Golang is that it relies on the source management path being the same as the one used within `go.mod` and in package imports within individual Go files. This makes working with forks a bit awkward.
|
One of the quirks of Golang is that it relies on the source management path being the same as the one used within `go.mod` and in package imports within individual Go files. This makes working with forks a bit awkward. The solution to this is to fork, then clone the upstream repository, then set `origin` of the upstream repository to that of your fork.
|
||||||
|
|
||||||
Let's say you fork GoToSocial to `github.com/yourgithubname/gotosocial`, and then clone that repository to `~/go/src/github.com/yourgithubname/gotosocial`. You will probably run into errors trying to run tests or build, so you might change your `go.mod` file so that the module is called `github.com/yourgithubname/gotosocial` instead of `github.com/superseriousbusiness/gotosocial`. But then this breaks all the imports within the project. Nightmare! So now you have to go through the source files and painstakingly replace `github.com/superseriousbusiness/gotosocial` with `github.com/yourgithubname/gotosocial`. This works OK, but when you decide to make a pull request against the original repo, all the changed paths are included! Argh!
|
|
||||||
|
|
||||||
The correct solution to this is to fork, then clone the upstream repository, then set `origin` of the upstream repository to that of your fork.
|
|
||||||
|
|
||||||
See [this blog post](https://blog.sgmansfield.com/2016/06/working-with-forks-in-go/) for more details.
|
See [this blog post](https://blog.sgmansfield.com/2016/06/working-with-forks-in-go/) for more details.
|
||||||
|
|
||||||
In case this post disappears, here are the steps (slightly modified):
|
In case this post disappears, here are the steps (slightly modified):
|
||||||
|
|
||||||
>
|
>
|
||||||
> Fork the repository on GitHub or set up whatever other remote git repo you will be using. In this case, I would go to GitHub and fork the repository.
|
> Fork the repository on Codeberg or set up whatever other remote git repo you will be using. In this case, I would go to Codeberg and fork the repository.
|
||||||
>
|
>
|
||||||
> Now clone the upstream repo (not the fork):
|
> Now clone the upstream repo (not the fork):
|
||||||
>
|
>
|
||||||
> `mkdir -p ~/go/src/github.com/superseriousbusiness && git clone git@github.com:superseriousbusiness/gotosocial ~/go/src/github.com/superseriousbusiness/gotosocial`
|
> `mkdir -p ~/go/src/code.superseriousbusiness.org && git clone git@codeberg.org:superseriousbusiness/gotosocial ~/go/src/code.superseriousbusiness.org/gotosocial`
|
||||||
>
|
>
|
||||||
> Navigate to the top level of the upstream repository on your computer:
|
> Navigate to the top level of the upstream repository on your computer:
|
||||||
>
|
>
|
||||||
> `cd ~/go/src/github.com/superseriousbusiness/gotosocial`
|
> `cd ~/go/src/code.superseriousbusiness.org/gotosocial`
|
||||||
>
|
>
|
||||||
> Rename the current origin remote to upstream:
|
> Rename the current origin remote to upstream:
|
||||||
>
|
>
|
||||||
|
|
@ -149,7 +146,7 @@ In case this post disappears, here are the steps (slightly modified):
|
||||||
>
|
>
|
||||||
> Add your fork as origin:
|
> Add your fork as origin:
|
||||||
>
|
>
|
||||||
> `git remote add origin git@github.com:yourgithubname/gotosocial`
|
> `git remote add origin git@codeberg.org:username/gotosocial`
|
||||||
>
|
>
|
||||||
|
|
||||||
Be sure to run `git fetch` before building the project for the first time.
|
Be sure to run `git fetch` before building the project for the first time.
|
||||||
|
|
@ -158,9 +155,9 @@ Be sure to run `git fetch` before building the project for the first time.
|
||||||
|
|
||||||
#### Binary
|
#### Binary
|
||||||
|
|
||||||
To get started, you first need to have Go installed. GtS is currently using Go 1.21, 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. Check the top of the `go.mod` file to see which version of Go you need to install, and see [here](https://golang.org/doc/install) for installation instructions.
|
||||||
|
|
||||||
Once you've got go installed, clone this repository into your Go path. Normally, this should be `~/go/src/github.com/superseriousbusiness/gotosocial`.
|
Once you've got Go installed, clone this repository into your Go path. Normally, this should be `~/go/src/code.superseriousbusiness.org/gotosocial`.
|
||||||
|
|
||||||
Once you've installed the prerequisites, you can try building the project: `./scripts/build.sh`. This will build the `gotosocial` binary.
|
Once you've installed the prerequisites, you can try building the project: `./scripts/build.sh`. This will build the `gotosocial` binary.
|
||||||
|
|
||||||
|
|
@ -176,7 +173,7 @@ nodemon -e go --signal SIGTERM --exec "go run ./cmd/gotosocial --host localhost
|
||||||
|
|
||||||
#### Docker
|
#### Docker
|
||||||
|
|
||||||
For both of the below methods, you need to have [Docker buildx](https://docs.docker.com/buildx/working-with-buildx/) installed.
|
For both of the below methods, you need to have [Docker buildx](https://docs.docker.com/build/concepts/overview/#buildx) installed.
|
||||||
|
|
||||||
##### With GoReleaser
|
##### With GoReleaser
|
||||||
|
|
||||||
|
|
@ -188,9 +185,7 @@ Normally, these processes are handled by Drone (see CI/CD above). However, you c
|
||||||
|
|
||||||
To do this, first [install GoReleaser](https://goreleaser.com/install/).
|
To do this, first [install GoReleaser](https://goreleaser.com/install/).
|
||||||
|
|
||||||
Then install GoSwagger as described in [the Swagger section](#updating-swagger-docs).
|
Then install Node and Yarn as described in [Stylesheet / Web dev](#stylesheet-web-dev).
|
||||||
|
|
||||||
Then install Node and Yarn as described in [Stylesheet / Web dev](#stylesheet--web-dev).
|
|
||||||
|
|
||||||
Finally, to create a snapshot build, do:
|
Finally, to create a snapshot build, do:
|
||||||
|
|
||||||
|
|
@ -202,7 +197,7 @@ If all goes according to plan, you should now have a number of multiple-architec
|
||||||
|
|
||||||
##### Manually
|
##### Manually
|
||||||
|
|
||||||
If you prefer a simple approach to building a Docker container, with fewer dependencies (go-swagger, Node, Yarn), you can also just build in the following way:
|
If you prefer a simple approach to building a Docker container, with fewer dependencies (Node, Yarn), you can also just build in the following way:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./scripts/build.sh && docker buildx build -t superseriousbusiness/gotosocial:latest .
|
./scripts/build.sh && docker buildx build -t superseriousbusiness/gotosocial:latest .
|
||||||
|
|
@ -210,6 +205,8 @@ If you prefer a simple approach to building a Docker container, with fewer depen
|
||||||
|
|
||||||
The above command first builds the `gotosocial` binary, then invokes Docker buildx to build the container image.
|
The above command first builds the `gotosocial` binary, then invokes Docker buildx to build the container image.
|
||||||
|
|
||||||
|
If you get an error while doing the build that looks like `"/web/assets/swagger.yaml": not found`, then you need to (re)generate the Swagger docs once, see [Updating Swagger docs](#updating-swagger-docs) for the command.
|
||||||
|
|
||||||
If you want to build a docker image for a different CPU architecture without setting up buildx (for example for ARMv7 aka 32-bit ARM), first modify the Dockerfile by adding the following lines to the top (but don't commit this!):
|
If you want to build a docker image for a different CPU architecture without setting up buildx (for example for ARMv7 aka 32-bit ARM), first modify the Dockerfile by adding the following lines to the top (but don't commit this!):
|
||||||
|
|
||||||
```dockerfile
|
```dockerfile
|
||||||
|
|
@ -397,7 +394,7 @@ If there's no output, great! It passed :)
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
|
|
||||||
GoToSocial provides a [testrig](https://github.com/superseriousbusiness/gotosocial/tree/main/testrig) with a number of mock packages you can use in integration tests.
|
GoToSocial provides a [testrig](https://codeberg.org/superseriousbusiness/gotosocial/tree/main/testrig) with a number of mock packages you can use in integration tests.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
|
|
@ -428,10 +425,35 @@ At the login screen, enter the email address `zork@example.org` and password `pa
|
||||||
|
|
||||||
Note the following constraints:
|
Note the following constraints:
|
||||||
|
|
||||||
- Since the testrig uses an in-memory database, the database will be destroyed when the testrig is stopped.
|
- Since the testrig uses an in-memory database by default, the database will be destroyed when the testrig is stopped.
|
||||||
- If you stop the testrig and start it again, any tokens or applications you created during your tests will also be removed. As such, you need to log out and in again every time you stop/start the rig.
|
- If you stop the testrig and start it again, by default any tokens or applications you created during your tests will also be removed. As such, you need to log out and in again every time you stop/start the rig.
|
||||||
- The testrig does not make any actual external HTTP calls, so federation will not work from a testrig.
|
- The testrig does not make any actual external HTTP calls, so federation will not work from a testrig.
|
||||||
|
|
||||||
|
##### Configuring the Standalone Testrig
|
||||||
|
|
||||||
|
By default the standalone testrig uses an in-memory SQLite database, which is filled with test data when starting up, and is cleared when shutting down, but you can tweak this (and a few other settings) with environment variables:
|
||||||
|
|
||||||
|
- `GTS_LOG_LEVEL` - you can set this to `trace` if you want to see all DB queries.
|
||||||
|
- `GTS_TESTRIG_SKIP_DB_SETUP` - set this to any value to skip the creation of tables and population of test data when the testrig starts.
|
||||||
|
- `GTS_TESTRIG_SKIP_DB_TEARDOWN` - set this to any value to skip the deletion of tables and test data when the testrig stops.
|
||||||
|
- `GTS_STORAGE_BACKEND` - this uses in-memory storage by default, but you can set this to `s3` to use a locally-running Minio etc for testing.
|
||||||
|
- `GTS_DB_TYPE` - you can change this to `postgres` to test against a locally-running Postgres intance.
|
||||||
|
- `GTS_DB_ADDRESS` - this is set to `:memory:` by default. You can change this to use an sqlite.db file somewhere, or set it to a Postgres address.
|
||||||
|
- `GTS_DB_PORT`, `GTS_DB_USER`, `GTS_DB_PASSWORD`, `GTS_DB_DATABASE`, `GTS_DB_TLS_MODE`, `GTS_DB_TLS_CA_CERT` - you can set these if you change `GTS_DB_ADDRESS` to `postgres` and don't use `GTS_DB_POSTGRES_CONNECTION_STRING`.
|
||||||
|
- `GTS_DB_POSTGRES_CONNECTION_STRING` - use this to provide a Postgres connection string if you don't want to set all the db env variables mentioned in the previous point.
|
||||||
|
|
||||||
|
Using these variables you can also (albeit awkwardly) test migrations from one schema to another.
|
||||||
|
|
||||||
|
For example, to test SQLite migrations:
|
||||||
|
|
||||||
|
1. Switch to main branch.
|
||||||
|
2. Build the debug binary, and then start the testrig with `DEBUG=1 GTS_LOG_LEVEL=trace GTS_DB_ADDRESS=./sqlite.test.db GTS_TESTRIG_SKIP_DB_TEARDOWN=1 ./gotosocial testrig start`. This instructs the testrig to use trace logging, use an actual file for the SQLite db, and to skip tearing it down when finished.
|
||||||
|
3. Stop the testrig.
|
||||||
|
4. The file `sqlite.test.db` now contains the schema and test models from the main branch.
|
||||||
|
5. Switch to the branch with the migration you want to test.
|
||||||
|
6. Build the debug binary, and then start the testrig with `DEBUG=1 GTS_LOG_LEVEL=trace GTS_DB_ADDRESS=./sqlite.test.db GTS_TESTRIG_SKIP_DB_SETUP=1 ./gotosocial testrig start`. This instructs the testrig to use trace logging, and to use the already-populated sqlite.test.db file.
|
||||||
|
7. You should see logging for migrations.
|
||||||
|
|
||||||
#### Running automated tests
|
#### Running automated tests
|
||||||
|
|
||||||
Tests can be run against both SQLite and Postgres.
|
Tests can be run against both SQLite and Postgres.
|
||||||
|
|
@ -484,65 +506,59 @@ You'll additionally need functioning DNS for your two instance names, which you
|
||||||
|
|
||||||
GoToSocial uses [go-swagger](https://goswagger.io) to generate Swagger API documentation from code annotations.
|
GoToSocial uses [go-swagger](https://goswagger.io) to generate Swagger API documentation from code annotations.
|
||||||
|
|
||||||
You can install go-swagger following the instructions [here](https://goswagger.io/install.html).
|
If you change Swagger annotations on any of the API paths, you can generate a new Swagger file at `./docs/api/swagger.yaml`, and copy that file to web assets, by running:
|
||||||
|
|
||||||
If you change Swagger annotations on any of the API paths, you can generate a new Swagger file at `./docs/api/swagger.yaml` by running:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go run github.com/go-swagger/go-swagger/cmd/swagger generate spec --scan-models --exclude-deps --output docs/api/swagger.yaml
|
go run ./vendor/github.com/go-swagger/go-swagger/cmd/swagger \
|
||||||
|
generate spec --scan-models --exclude-deps -o docs/api/swagger.yaml \
|
||||||
|
&& cp docs/api/swagger.yaml web/assets/swagger.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You shouldn't need to install go-swagger to run this command, as it's already included in the `vendor` folder.
|
||||||
|
|
||||||
### CI/CD configuration
|
### CI/CD configuration
|
||||||
|
|
||||||
GoToSocial uses [Drone](https://www.drone.io/) for CI/CD tasks like running tests, linting, and building Docker containers.
|
GoToSocial uses [Woodpecker CI](https://woodpecker-ci.org/) for CI/CD tasks like running tests, linting, and building Docker containers.
|
||||||
|
|
||||||
These runs are integrated with GitHub, and will be run on opening a pull request or merging into main.
|
These runs are integrated with Codeberg, and will be run on opening a pull request or merging into main.
|
||||||
|
|
||||||
The Drone instance for GoToSocial is [here](https://drone.superseriousbusiness.org/superseriousbusiness/gotosocial).
|
The `woodpecker` pipeline files are in the `.woodpecker` directory of this repository — these define how and when Woodpecker should run.
|
||||||
|
|
||||||
The `drone.yml` file is [here](./.drone.yml) — this defines how and when Drone should run. Documentation for Drone is [here](https://docs.drone.io/).
|
The Woodpecker instance for GoToSocial is [here](https://woodpecker.superseriousbusiness.org/repos/2).
|
||||||
|
|
||||||
It is worth noting that the `drone.yml` file must be signed by the Drone admin account to be considered valid. This must be done every time the file is changed. This is to prevent tampering and hijacking of the Drone instance. See [here](https://docs.drone.io/signature/).
|
Documentation for Woodpecker is [here](https://woodpecker-ci.org/docs/intro).
|
||||||
|
|
||||||
To sign the file, first install and setup the [drone cli tool](https://docs.drone.io/cli/install/). Then, run:
|
## Other Useful Stuff
|
||||||
|
|
||||||
|
Various bits and bobs.
|
||||||
|
|
||||||
|
### Running migrations on a Postgres DB backup locally
|
||||||
|
|
||||||
|
It may be useful when testing or debugging migrations to be able to run them against a copy of a real instance's Postgres database locally.
|
||||||
|
|
||||||
|
Basic steps for this:
|
||||||
|
|
||||||
|
First dump the Postgres database on the remote machine, and copy the dump over to your development machine.
|
||||||
|
|
||||||
|
Now create a local Postgres container and mount the dump into it with, for example:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
drone -t PUT_YOUR_DRONE_ADMIN_TOKEN_HERE -s https://drone.superseriousbusiness.org sign superseriousbusiness/gotosocial --save
|
docker run -it --name postgres --network host -e POSTGRES_PASSWORD=postgres -v /path/to/db_dump:/db_dump postgres
|
||||||
```
|
```
|
||||||
|
|
||||||
### Release Checklist
|
In a separate terminal window, execute a command inside the running container to load the dump into the "postgres" database:
|
||||||
|
|
||||||
First things first: If this is a security hot-fix, we'll probably rush through this list, and make a prettier release a few days later.
|
```bash
|
||||||
|
docker exec -it --user postgres postgres psql -X -f /db_dump postgres
|
||||||
|
```
|
||||||
|
|
||||||
Now, with that out of the way, here's our Checklist.
|
With the Postgres container still running, run GoToSocial and point it towards the container. Use the appropriate `GTS_HOST` (and `GTS_ACCOUNT_DOMAIN`) values for the instance you dumped:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
GTS_HOST=example.org \
|
||||||
|
GTS_DB_TYPE=postgres \
|
||||||
|
GTS_DB_POSTGRES_CONNECTION_STRING=postgres://postgres:postgres@localhost:5432/postgres \
|
||||||
|
./gotosocial migrations run
|
||||||
|
```
|
||||||
|
|
||||||
GoToSocial follows [Semantic Versioning](https://semver.org/).
|
When you're done messing around, don't forget to remove any containers that you started up, and remove any lingering volumes with `docker volume prune`, else you might end up filling your disk with unused temporary volumes.
|
||||||
So our first concern on the Checklist is:
|
|
||||||
|
|
||||||
- What version are we releasing?
|
|
||||||
|
|
||||||
Next we need to check:
|
|
||||||
|
|
||||||
- Do the assets have to be rebuilt and committed to the repository.
|
|
||||||
- Do the swagger docs have to be rebuilt?
|
|
||||||
|
|
||||||
On the project management side:
|
|
||||||
|
|
||||||
- Are there any issues that have to be moved to a different milestone?
|
|
||||||
- Are there any things on the [Roadmap](./ROADMAP.md) that can be ticked off?
|
|
||||||
|
|
||||||
Once we're happy with our Checklist, we can create the tag, and push it.
|
|
||||||
And the rest [is automation](./.drone.yml).
|
|
||||||
|
|
||||||
We can now head to GitHub, and add some personality to the release notes.
|
|
||||||
Finally, we make announcements on the all our channels that the release is out!
|
|
||||||
|
|
||||||
#### What if something goes wrong?
|
|
||||||
|
|
||||||
Sometimes things go awry.
|
|
||||||
We release a buggy release, we forgot something something important.
|
|
||||||
|
|
||||||
If the release is so bad that it's unusable or dangerous! to a great part of our user-base, we can pull.
|
|
||||||
That is: Delete the tag.
|
|
||||||
|
|
||||||
Either way, once we've resolved the issue, we just start from the top of this list again. Version numbers are cheap. It's cheap to burn them.
|
|
||||||
|
|
|
||||||
27
Dockerfile
27
Dockerfile
|
|
@ -1,25 +1,8 @@
|
||||||
# syntax=docker/dockerfile:1.3
|
# syntax=docker/dockerfile:1.3
|
||||||
# Dockerfile reference: https://docs.docker.com/engine/reference/builder/
|
# Dockerfile reference: https://docs.docker.com/engine/reference/builder/
|
||||||
|
|
||||||
# stage 1: generate up-to-date swagger.yaml to put in the final container
|
# stage 1: generate the web/assets/dist bundles
|
||||||
FROM --platform=${BUILDPLATFORM} golang:1.23-alpine AS swagger
|
FROM --platform=${BUILDPLATFORM} node:lts-alpine AS bundler
|
||||||
|
|
||||||
RUN \
|
|
||||||
### Installs goswagger for building swagger definitions inside this container
|
|
||||||
go install "github.com/go-swagger/go-swagger/cmd/swagger@c46c303aaa02" && \
|
|
||||||
# Makes swagger executable
|
|
||||||
chmod +x /go/bin/swagger
|
|
||||||
|
|
||||||
COPY go.mod /go/src/github.com/superseriousbusiness/gotosocial/go.mod
|
|
||||||
COPY go.sum /go/src/github.com/superseriousbusiness/gotosocial/go.sum
|
|
||||||
COPY cmd /go/src/github.com/superseriousbusiness/gotosocial/cmd
|
|
||||||
COPY internal /go/src/github.com/superseriousbusiness/gotosocial/internal
|
|
||||||
|
|
||||||
WORKDIR /go/src/github.com/superseriousbusiness/gotosocial
|
|
||||||
RUN /go/bin/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:18-alpine AS bundler
|
|
||||||
|
|
||||||
COPY web web
|
COPY web web
|
||||||
RUN yarn --cwd ./web/source install && \
|
RUN yarn --cwd ./web/source install && \
|
||||||
|
|
@ -27,8 +10,8 @@ RUN yarn --cwd ./web/source install && \
|
||||||
yarn --cwd ./web/source build && \
|
yarn --cwd ./web/source build && \
|
||||||
rm -rf ./web/source
|
rm -rf ./web/source
|
||||||
|
|
||||||
# stage 3: build the executor container
|
# stage 2: build the executor container
|
||||||
FROM --platform=${TARGETPLATFORM} alpine:3.20 as executor
|
FROM --platform=${TARGETPLATFORM} alpine:3.21 AS executor
|
||||||
|
|
||||||
# switch to non-root user:group for GtS
|
# switch to non-root user:group for GtS
|
||||||
USER 1000:1000
|
USER 1000:1000
|
||||||
|
|
@ -50,7 +33,7 @@ COPY --chown=1000:1000 gotosocial /gotosocial/gotosocial
|
||||||
|
|
||||||
# copy over the web directories with templates, assets etc
|
# copy over the web directories with templates, assets etc
|
||||||
COPY --chown=1000:1000 --from=bundler web /gotosocial/web
|
COPY --chown=1000:1000 --from=bundler web /gotosocial/web
|
||||||
COPY --chown=1000:1000 --from=swagger /go/src/github.com/superseriousbusiness/gotosocial/swagger.yaml web/assets/swagger.yaml
|
COPY --chown=1000:1000 ./web/assets/swagger.yaml /gotosocial/web/assets/swagger.yaml
|
||||||
|
|
||||||
VOLUME [ "/gotosocial/storage", "/gotosocial/.cache" ]
|
VOLUME [ "/gotosocial/storage", "/gotosocial/.cache" ]
|
||||||
ENTRYPOINT [ "/gotosocial/gotosocial", "server", "start" ]
|
ENTRYPOINT [ "/gotosocial/gotosocial", "server", "start" ]
|
||||||
|
|
|
||||||
134
README.md
134
README.md
|
|
@ -3,23 +3,23 @@
|
||||||
|
|
||||||
**Update regarding corporate sponsors: we are open to sponsorship arrangements with organizations that align with our values; see the conditions below**
|
**Update regarding corporate sponsors: we are open to sponsorship arrangements with organizations that align with our values; see the conditions below**
|
||||||
|
|
||||||
GoToSocial is an [ActivityPub](https://activitypub.rocks/) social network server, written in Golang.
|
🏳️🌈 GoToSocial is an [ActivityPub](https://activitypub.rocks/) social network server, written in Golang. 🏳️⚧️
|
||||||
|
|
||||||
With GoToSocial, you can keep in touch with your friends, post, read, and share images and articles. All without being tracked or advertised to!
|
With GoToSocial, you can keep in touch with your friends, post, read, and share images and articles. All without being tracked or advertised to!
|
||||||
|
|
||||||
<p align="middle">
|
<p align="middle">
|
||||||
<img src="https://raw.githubusercontent.com/superseriousbusiness/gotosocial/main/docs/overrides/public/sloth.webp" width="300"/>
|
<img src="https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/docs/overrides/public/sloth.webp" width="300"/>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
**GoToSocial is still [BETA SOFTWARE](https://en.wikipedia.org/wiki/Software_release_life_cycle#Beta)**. 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 left alpha stage around September/October 2024, and we intend to exit beta some time around 2026.
|
**GoToSocial is still [BETA SOFTWARE](https://en.wikipedia.org/wiki/Software_release_life_cycle#Beta)**. 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 left alpha stage around September/October 2024, and we intend to exit beta some time around 2026.
|
||||||
|
|
||||||
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/).
|
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](https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md) file.
|
To build from source, check the [CONTRIBUTING.md](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/CONTRIBUTING.md) file.
|
||||||
|
|
||||||
Here's a screenshot of the instance landing page! Check out the project's [official account](https://gts.superseriousbusiness.org/@gotosocial) running on GoToSocial.
|
Here's a screenshot of the instance landing page! Check out the project's [official account](https://gts.superseriousbusiness.org/@gotosocial) running on GoToSocial.
|
||||||
|
|
||||||

|

|
||||||
<!--overview-end-->
|
<!--overview-end-->
|
||||||
|
|
||||||
## Table of Contents <!-- omit in toc -->
|
## Table of Contents <!-- omit in toc -->
|
||||||
|
|
@ -36,10 +36,11 @@ Here's a screenshot of the instance landing page! Check out the project's [offic
|
||||||
- [Rich text formatting](#rich-text-formatting)
|
- [Rich text formatting](#rich-text-formatting)
|
||||||
- [Themes and custom CSS](#themes-and-custom-css)
|
- [Themes and custom CSS](#themes-and-custom-css)
|
||||||
- [Easy to run](#easy-to-run)
|
- [Easy to run](#easy-to-run)
|
||||||
- [Safety + security features](#safety--security-features)
|
- [Safety + security features](#safety-security-features)
|
||||||
- [Various federation modes](#various-federation-modes)
|
- [Various federation modes](#various-federation-modes)
|
||||||
- [OIDC integration](#oidc-integration)
|
- [OIDC integration](#oidc-integration)
|
||||||
- [Backend-first design](#backend-first-design)
|
- [Backend-first design](#backend-first-design)
|
||||||
|
- [Alternatives to GoToSocial](#alternatives-to-gotosocial)
|
||||||
- [Known Issues](#known-issues)
|
- [Known Issues](#known-issues)
|
||||||
- [Installing GoToSocial](#installing-gotosocial)
|
- [Installing GoToSocial](#installing-gotosocial)
|
||||||
- [Supported Platforms](#supported-platforms)
|
- [Supported Platforms](#supported-platforms)
|
||||||
|
|
@ -50,7 +51,7 @@ Here's a screenshot of the instance landing page! Check out the project's [offic
|
||||||
- [Stable Releases](#stable-releases)
|
- [Stable Releases](#stable-releases)
|
||||||
- [Snapshot Releases](#snapshot-releases)
|
- [Snapshot Releases](#snapshot-releases)
|
||||||
- [Docker](#docker)
|
- [Docker](#docker)
|
||||||
- [Binary release .tar.gz](#binary-release-targz)
|
- [Binary release .tar.gz](#binary-release-tar-gz)
|
||||||
- [From Source](#from-source)
|
- [From Source](#from-source)
|
||||||
- [Third-party Packaging](#third-party-packaging)
|
- [Third-party Packaging](#third-party-packaging)
|
||||||
- [Contributing](#contributing)
|
- [Contributing](#contributing)
|
||||||
|
|
@ -60,7 +61,7 @@ Here's a screenshot of the instance landing page! Check out the project's [offic
|
||||||
- [Image Attribution and Licensing](#image-attribution-and-licensing)
|
- [Image Attribution and Licensing](#image-attribution-and-licensing)
|
||||||
- [Team](#team)
|
- [Team](#team)
|
||||||
- [Special Thanks](#special-thanks)
|
- [Special Thanks](#special-thanks)
|
||||||
- [Sponsorship + Funding](#sponsorship--funding)
|
- [Sponsorship + Funding](#sponsorship-funding)
|
||||||
- [Crowdfunding](#crowdfunding)
|
- [Crowdfunding](#crowdfunding)
|
||||||
- [Corporate Sponsorship](#corporate-sponsorship)
|
- [Corporate Sponsorship](#corporate-sponsorship)
|
||||||
- [NLnet](#nlnet)
|
- [NLnet](#nlnet)
|
||||||
|
|
@ -73,7 +74,7 @@ GoToSocial provides a lightweight, customizable, and safety-focused entryway int
|
||||||
|
|
||||||
If you've ever used something like Twitter or Tumblr (or even Myspace!) GoToSocial will probably feel familiar to you: You can follow people and have followers, you make posts which people can favourite and reply to and share, and you scroll through posts from people you follow using a timeline. You can write long posts or short posts, or just post images, it's up to you. You can also, of course, block people or otherwise limit interactions that you don't want by posting just to your friends.
|
If you've ever used something like Twitter or Tumblr (or even Myspace!) GoToSocial will probably feel familiar to you: You can follow people and have followers, you make posts which people can favourite and reply to and share, and you scroll through posts from people you follow using a timeline. You can write long posts or short posts, or just post images, it's up to you. You can also, of course, block people or otherwise limit interactions that you don't want by posting just to your friends.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**GoToSocial does NOT use recommendation algorithms or collect data about you to suggest content or 'improve your experience'**. The timeline is chronological: whatever you see at the top of your timeline is there because it's *just been posted*, not because it's been selected as interesting (or controversial) based on your personal profile.
|
**GoToSocial does NOT use recommendation algorithms or collect data about you to suggest content or 'improve your experience'**. The timeline is chronological: whatever you see at the top of your timeline is there because it's *just been posted*, not because it's been selected as interesting (or controversial) based on your personal profile.
|
||||||
|
|
||||||
|
|
@ -85,7 +86,7 @@ GoToSocial doesn't claim to be *better* than any other application, but it offer
|
||||||
|
|
||||||
Because GoToSocial uses [ActivityPub](https://activitypub.rocks/), you can hang out not just with people on your home server, but with people all over the [Fediverse](https://en.wikipedia.org/wiki/Fediverse), seamlessly.
|
Because GoToSocial uses [ActivityPub](https://activitypub.rocks/), you can hang out not just with people on your home server, but with people all over the [Fediverse](https://en.wikipedia.org/wiki/Fediverse), seamlessly.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Federation means that your home server is part of a network of servers all over the world that all communicate using the same protocol. Your data is no longer centralized on one company's servers, but resides on your own server and is shared — as you see fit — across a resilient web of servers run by other people.
|
Federation means that your home server is part of a network of servers all over the world that all communicate using the same protocol. Your data is no longer centralized on one company's servers, but resides on your own server and is shared — as you see fit — across a resilient web of servers run by other people.
|
||||||
|
|
||||||
|
|
@ -101,7 +102,7 @@ It began as a solo project, and then picked up steam as more developers became i
|
||||||
|
|
||||||
We made our first Alpha release in November 2021. We left Alpha and entered Beta in September/October 2024.
|
We made our first Alpha release in November 2021. We left Alpha and entered Beta in September/October 2024.
|
||||||
|
|
||||||
For a detailed view on what's implemented and what's not, and progress made towards [stable release](https://en.wikipedia.org/wiki/Software_release_life_cycle#Stable_release), please see [the roadmap document](https://github.com/superseriousbusiness/gotosocial/blob/main/ROADMAP.md).
|
For a detailed view on what's implemented and what's not, and progress made towards [stable release](https://en.wikipedia.org/wiki/Software_release_life_cycle#Stable_release), please see [the roadmap document](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/ROADMAP.md).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -129,11 +130,11 @@ GoToSocial offers public, unlisted/unlocked, followers-only, and direct posts (s
|
||||||
|
|
||||||
GoToSocial lets you choose who can reply to your posts, via [interaction policies](https://docs.gotosocial.org/en/latest/user_guide/settings/#default-interaction-policies). You can choose to let anyone reply to your posts, let only your friends reply, and more.
|
GoToSocial lets you choose who can reply to your posts, via [interaction policies](https://docs.gotosocial.org/en/latest/user_guide/settings/#default-interaction-policies). You can choose to let anyone reply to your posts, let only your friends reply, and more.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### Local-only posting
|
### Local-only posting
|
||||||
|
|
||||||
Sometimes you only want to talk to people you share an instance with. GoToSocial supports this via local-only posting, which ensures that your post stays on your instance only. (Local-only posting is currently dependent on client support.)
|
Sometimes you only want to talk to people you share an instance with. GoToSocial supports this via local-only posting, which ensures that your post stays on your instance only, even if it gets boosted. They also do not show up in the web interface, and are not accessible publicly via URL. (Local-only posting is currently dependent on client support.)
|
||||||
|
|
||||||
### RSS feed
|
### RSS feed
|
||||||
|
|
||||||
|
|
@ -143,7 +144,7 @@ GoToSocial lets you opt-in to exposing your profile as an RSS feed, so that peop
|
||||||
|
|
||||||
With GoToSocial, you can write posts using the popular, easy-to-use Markdown markup language, which lets you produce rich HTML posts with support for blockquotes, syntax-highlighted code blocks, lists, inline links, and more.
|
With GoToSocial, you can write posts using the popular, easy-to-use Markdown markup language, which lets you produce rich HTML posts with support for blockquotes, syntax-highlighted code blocks, lists, inline links, and more.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### Themes and custom CSS
|
### Themes and custom CSS
|
||||||
|
|
||||||
|
|
@ -154,71 +155,91 @@ It's also easy for admins to [add their own custom themes](https://docs.gotosoci
|
||||||
<details>
|
<details>
|
||||||
<summary>Show theme examples</summary>
|
<summary>Show theme examples</summary>
|
||||||
<figure>
|
<figure>
|
||||||
<img src="https://raw.githubusercontent.com/superseriousbusiness/gotosocial/main/docs/overrides/public/theme-blurple-dark.png"/>
|
<img src="https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/docs/overrides/public/theme-blurple-dark.png"/>
|
||||||
<figcaption>Blurple dark</figcaption>
|
<figcaption>Blurple dark</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
<hr/>
|
<hr/>
|
||||||
<figure>
|
<figure>
|
||||||
<img src="https://raw.githubusercontent.com/superseriousbusiness/gotosocial/main/docs/overrides/public/theme-blurple-light.png"/>
|
<img src="https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/docs/overrides/public/theme-blurple-light.png"/>
|
||||||
<figcaption>Blurple light</figcaption>
|
<figcaption>Blurple light</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
<hr/>
|
<hr/>
|
||||||
<figure>
|
<figure>
|
||||||
<img src="https://raw.githubusercontent.com/superseriousbusiness/gotosocial/main/docs/overrides/public/theme-brutalist-light.png"/>
|
<img src="https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/docs/overrides/public/theme-brutalist-light.png"/>
|
||||||
<figcaption>Brutalist light</figcaption>
|
<figcaption>Brutalist light</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
<hr/>
|
<hr/>
|
||||||
<figure>
|
<figure>
|
||||||
<img src="https://raw.githubusercontent.com/superseriousbusiness/gotosocial/main/docs/overrides/public/theme-brutalist-dark.png"/>
|
<img src="https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/docs/overrides/public/theme-brutalist-dark.png"/>
|
||||||
<figcaption>Brutalist dark</figcaption>
|
<figcaption>Brutalist dark</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
<hr/>
|
<hr/>
|
||||||
<figure>
|
<figure>
|
||||||
<img src="https://raw.githubusercontent.com/superseriousbusiness/gotosocial/main/docs/overrides/public/theme-ecks-pee.png"/>
|
<img src="https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/docs/overrides/public/theme-ecks-pee.png"/>
|
||||||
<figcaption>Ecks pee</figcaption>
|
<figcaption>Ecks pee</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
<hr/>
|
<hr/>
|
||||||
<figure>
|
<figure>
|
||||||
<img src="https://raw.githubusercontent.com/superseriousbusiness/gotosocial/main/docs/overrides/public/theme-midnight-trip.png"/>
|
<img src="https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/docs/overrides/public/theme-midnight-trip.png"/>
|
||||||
<figcaption>Midnight trip</figcaption>
|
<figcaption>Midnight trip</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
<figure>
|
<figure>
|
||||||
<img src="https://raw.githubusercontent.com/superseriousbusiness/gotosocial/main/docs/overrides/public/theme-moonlight-hunt.png"/>
|
<img src="https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/docs/overrides/public/theme-moonlight-hunt.png"/>
|
||||||
<figcaption>Moonlight hunt</figcaption>
|
<figcaption>Moonlight hunt</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
<hr/>
|
<hr/>
|
||||||
<figure>
|
<figure>
|
||||||
<img src="https://raw.githubusercontent.com/superseriousbusiness/gotosocial/main/docs/overrides/public/theme-rainforest.png"/>
|
<img src="https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/docs/overrides/public/theme-rainforest.png"/>
|
||||||
<figcaption>Rainforest</figcaption>
|
<figcaption>Rainforest</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
<hr/>
|
<hr/>
|
||||||
<figure>
|
<figure>
|
||||||
<img src="https://raw.githubusercontent.com/superseriousbusiness/gotosocial/main/docs/overrides/public/theme-soft.png"/>
|
<img src="https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/docs/overrides/public/theme-soft.png"/>
|
||||||
<figcaption>Soft</figcaption>
|
<figcaption>Soft</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
<hr/>
|
<hr/>
|
||||||
<figure>
|
<figure>
|
||||||
<img src="https://raw.githubusercontent.com/superseriousbusiness/gotosocial/main/docs/overrides/public/theme-solarized-dark.png"/>
|
<img src="https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/docs/overrides/public/theme-solarized-dark.png"/>
|
||||||
<figcaption>Solarized dark</figcaption>
|
<figcaption>Solarized dark</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
<hr/>
|
<hr/>
|
||||||
<figure>
|
<figure>
|
||||||
<img src="https://raw.githubusercontent.com/superseriousbusiness/gotosocial/main/docs/overrides/public/theme-solarized-light.png"/>
|
<img src="https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/docs/overrides/public/theme-solarized-light.png"/>
|
||||||
<figcaption>Solarized light</figcaption>
|
<figcaption>Solarized light</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
<hr/>
|
<hr/>
|
||||||
<figure>
|
<figure>
|
||||||
<img src="https://raw.githubusercontent.com/superseriousbusiness/gotosocial/main/docs/overrides/public/theme-sunset.png"/>
|
<img src="https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/docs/overrides/public/theme-sunset.png"/>
|
||||||
<figcaption>Sunset</figcaption>
|
<figcaption>Sunset</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
<hr/>
|
<hr/>
|
||||||
|
<figure>
|
||||||
|
<img src="https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/docs/overrides/public/theme-hacker-dark.png"/>
|
||||||
|
<figcaption>Hacker dark</figcaption>
|
||||||
|
</figure>
|
||||||
|
<hr/>
|
||||||
|
<figure>
|
||||||
|
<img src="https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/docs/overrides/public/theme-hacker-light.png"/>
|
||||||
|
<figcaption>Hacker light</figcaption>
|
||||||
|
</figure>
|
||||||
|
<hr/>
|
||||||
|
<figure>
|
||||||
|
<img src="https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/docs/overrides/public/theme-programmer-socks-dark.png"/>
|
||||||
|
<figcaption>Programmer socks dark</figcaption>
|
||||||
|
</figure>
|
||||||
|
<hr/>
|
||||||
|
<figure>
|
||||||
|
<img src="https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/docs/overrides/public/theme-programmer-socks-light.png"/>
|
||||||
|
<figcaption>Programmer socks light</figcaption>
|
||||||
|
</figure>
|
||||||
|
<hr/>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### Easy to run
|
### Easy to run
|
||||||
|
|
||||||
GoToSocial uses only about 250-350MiB of RAM, and requires very little CPU power, so it plays nice with single-board computers, old laptops and tiny $5/month VPSes.
|
GoToSocial uses only about 250-350MiB of RAM, and requires very little CPU power, so it plays nice with single-board computers, old laptops and tiny $5/month VPSes.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
No external dependencies apart from a database (or just use SQLite!).
|
No external dependencies apart from a database (or just use SQLite!).
|
||||||
|
|
||||||
|
|
@ -231,6 +252,7 @@ Simply download the binary + assets (or Docker container), tweak your configurat
|
||||||
- [Import, export](https://docs.gotosocial.org/en/latest/admin/settings/#importexport), and [subscribe](https://docs.gotosocial.org/en/latest/admin/domain_permission_subscriptions) to community-created domain allow and domain block lists.
|
- [Import, export](https://docs.gotosocial.org/en/latest/admin/settings/#importexport), and [subscribe](https://docs.gotosocial.org/en/latest/admin/domain_permission_subscriptions) to community-created domain allow and domain block lists.
|
||||||
- HTTP signature authentication: GoToSocial requires [HTTP Signatures](https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12) when sending and receiving messages, to ensure that your messages can't be tampered with and your identity can't be forged.
|
- HTTP signature authentication: GoToSocial requires [HTTP Signatures](https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12) when sending and receiving messages, to ensure that your messages can't be tampered with and your identity can't be forged.
|
||||||
- Built-in, automatic support for secure HTTPS with [Let's Encrypt](https://letsencrypt.org/).
|
- Built-in, automatic support for secure HTTPS with [Let's Encrypt](https://letsencrypt.org/).
|
||||||
|
- Support for two-factor authentication via time-based one-time passwords (Google authenticator, LastPass authenticator, etc).
|
||||||
|
|
||||||
### Various federation modes
|
### Various federation modes
|
||||||
|
|
||||||
|
|
@ -256,17 +278,29 @@ On top of this API, web developers are encouraged to build any front-end impleme
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Alternatives to GoToSocial
|
||||||
|
|
||||||
|
Don't like GtS but still want to use the fediverse? Like GtS but prefer not to use beta software? There are lots of alternatives that might suit you better! Here are some that we know work well (alphabetical order):
|
||||||
|
|
||||||
|
- [Akkoma](https://akkoma.social/): Full-featured ActivityPub microblogging with emoji reacts and quote posts (Elixir).
|
||||||
|
- [Honk](https://humungus.tedunangst.com/r/honk/m/activitypub.7): Minimalist, opinionated microblogging with "No likes, no faves, no polls, no stars, no claps, no counts." (Go).
|
||||||
|
- [Iceshrimp.net](https://iceshrimp.dev/iceshrimp/Iceshrimp.NET): rewrite of Iceshrimp from the ground up (.Net).
|
||||||
|
- [Mastodon](https://joinmastodon.org/): Actively developed, widely recognized, scaleable ActivityPub microblogging instance (Ruby).
|
||||||
|
- [Snac2](https://codeberg.org/grunfink/snac2): Simple, minimalistic instance with very low system requirements (portable C).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Known Issues
|
## Known Issues
|
||||||
|
|
||||||
Since GoToSocial is still in beta, there are plenty of bugs. We use [GitHub issues](https://github.com/superseriousbusiness/gotosocial/issues?q=is%3Aissue+is%3Aopen+label%3Abug) to track these.
|
Since GoToSocial is still in beta, there are plenty of bugs. We use [Codeberg issues](https://codeberg.org/superseriousbusiness/gotosocial/issues?labels=378161) to track these.
|
||||||
|
|
||||||
Since every ActivityPub server implementation has a slightly different interpretation of the protocol, some servers don't quite federate properly with GoToSocial yet. We're tracking these issues [in this project](https://github.com/superseriousbusiness/gotosocial/projects/4). Eventually, we want to make sure that any implementation that can federate nicely with Mastodon should also be able to federate with GoToSocial.
|
Since every ActivityPub server implementation has a slightly different interpretation of the protocol, some servers don't quite federate properly with GoToSocial yet. We're tracking these issues [with the federation label](https://codeberg.org/superseriousbusiness/gotosocial/issues?labels=378188). Eventually, we want to make sure that any implementation that can federate nicely with Mastodon should also be able to federate with GoToSocial.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Installing GoToSocial
|
## Installing GoToSocial
|
||||||
|
|
||||||
Check our [getting started](https://docs.gotosocial.org/en/latest/getting_started/) documentation! And have a peruse of our [releases page](https://github.com/superseriousbusiness/gotosocial/releases).
|
Check our [getting started](https://docs.gotosocial.org/en/latest/getting_started/) documentation! And have a peruse of our [releases page](https://codeberg.org/superseriousbusiness/gotosocial/releases).
|
||||||
|
|
||||||
<!--releases-start-->
|
<!--releases-start-->
|
||||||
### Supported Platforms
|
### Supported Platforms
|
||||||
|
|
@ -292,13 +326,13 @@ This is the current status of support offered by GoToSocial for different platfo
|
||||||
|
|
||||||
#### 64-bit
|
#### 64-bit
|
||||||
|
|
||||||
64-bit platforms require the following (now, very common) CPU features:
|
Notes on 64-bit CPU feature requirements:
|
||||||
|
|
||||||
- x86-64 require SSE4.1 (for both media decoding and WASM SQLite)
|
- x86_64 requires the [x86-64-v2](https://en.wikipedia.org/wiki/X86-64-v2) level instruction sets. (CPUs manufactured after ~2010)
|
||||||
|
|
||||||
- Armv8 require ARM64 Large System Extensions (specifically when using WASM SQLite)
|
- ARM64 requires no specific features, ARMv8 CPUs (and later) have all required features.
|
||||||
|
|
||||||
Without these features, performance will suffer. In these situations, you may have some success building a binary yourself with the totally **unsupported, experimental** [nowasm](https://docs.gotosocial.org/en/latest/advanced/builds/nowasm/) tag.
|
If any of the above features are missing, performance of media processing (and possibly, SQLite) will suffer. In these situations, you may have some success building a binary yourself with the totally **unsupported, experimental** [nowasm](https://docs.gotosocial.org/en/latest/advanced/builds/nowasm/) tag.
|
||||||
|
|
||||||
#### BSDs
|
#### BSDs
|
||||||
|
|
||||||
|
|
@ -322,7 +356,7 @@ While we don't support running GtS on OpenBSD, you may have some success buildin
|
||||||
|
|
||||||
We package our stable releases for both binary builds and Docker containers, so that you don't have to build from source yourself.
|
We package our stable releases for both binary builds and Docker containers, so that you don't have to build from source yourself.
|
||||||
|
|
||||||
The Docker image `superseriousbusiness/gotosocial:latest` will always correspond to the latest stable release. Since this tag is overwritten frequently, you may want to use Docker CLI flag `--pull always` to ensure that you always have the most up-to-date image every time you run using this tag. Alternatively, run `docker pull superseriousbusiness/gotosocial:latest` manually just before use.
|
The Docker image `docker.io/superseriousbusiness/gotosocial:latest` will always correspond to the latest stable release. Since this tag is overwritten frequently, you may want to use Docker CLI flag `--pull always` to ensure that you always have the most up-to-date image every time you run using this tag. Alternatively, run `docker pull docker.io/superseriousbusiness/gotosocial:latest` manually just before use.
|
||||||
|
|
||||||
### Snapshot Releases
|
### Snapshot Releases
|
||||||
|
|
||||||
|
|
@ -332,17 +366,17 @@ Please be warned that you do so at your own risk! We try to keep main working pr
|
||||||
|
|
||||||
#### Docker
|
#### Docker
|
||||||
|
|
||||||
To run from main using Docker, use the `snapshot` Docker tag. The Docker image `superseriousbusiness/gotosocial:snapshot` will always correspond to the latest commit on main. Since this tag is overwritten frequently, you may want to use Docker CLI flag `--pull always` to ensure that you always have the most up-to-date image every time you run using this tag. Alternatively, run `docker pull superseriousbusiness/gotosocial:snapshot` manually just before use.
|
To run from main using Docker, use the `snapshot` Docker tag. The Docker image `docker.io/superseriousbusiness/gotosocial:snapshot` will always correspond to the latest commit on main that involves changes to Go code or web assets/source. Since this tag is overwritten frequently, you may want to use Docker CLI flag `--pull always` to ensure that you always have the most up-to-date image every time you run using this tag. Alternatively, run `docker pull docker.io/superseriousbusiness/gotosocial:snapshot` manually just before use.
|
||||||
|
|
||||||
#### Binary release .tar.gz
|
#### Binary release .tar.gz
|
||||||
|
|
||||||
To run from main using a binary release, download the appropriate .tar.gz file for your architecture from our [self-hosted Minio S3 repository](https://minio.s3.superseriousbusiness.org/browser/gotosocial-snapshots).
|
To run from main using a binary release, download the appropriate .tar.gz file for your architecture from our [self-hosted Minio S3 repository](https://minio.s3.superseriousbusiness.org/browser/gotosocial-snapshots).
|
||||||
|
|
||||||
Snapshot binary releases in the S3 bucket are keyed by Github commit hash. To get the latest one, sort by Last Modified, or check out the list of commits [here](https://github.com/superseriousbusiness/gotosocial/commits/main), copy the SHA of the latest one, and paste it in the Minio console filter. Snapshot binary releases are expired after 28 days, to keep our hosting costs down.
|
Snapshot binary releases in the S3 bucket are keyed by commit hash. To get the latest one, sort by Last Modified, or check out the list of commits [here](https://codeberg.org/superseriousbusiness/gotosocial/commits/main), copy the SHA of the latest one, and paste it in the Minio console filter. Snapshot binary releases are expired after 28 days, to keep our hosting costs down.
|
||||||
|
|
||||||
### From Source
|
### From Source
|
||||||
|
|
||||||
Instructions for building GoToSocial from source are in the [CONTRIBUTING.md](https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md) file.
|
Instructions for building GoToSocial from source are in the [CONTRIBUTING.md](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/CONTRIBUTING.md) file.
|
||||||
|
|
||||||
### Third-party Packaging
|
### Third-party Packaging
|
||||||
|
|
||||||
|
|
@ -363,7 +397,7 @@ You can also deploy your own instance of GoToSocial with the help of:
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
You would like to contribute to GtS? Great! ❤️❤️❤️ Check out the issues page to see if there's anything you intend to jump in on, and read the [CONTRIBUTING.md](https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md) file for guidelines and setting up your dev environment.
|
You would like to contribute to GtS? Great! ❤️❤️❤️ Check out the issues page to see if there's anything you intend to jump in on, and read the [CONTRIBUTING.md](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/CONTRIBUTING.md) file for guidelines and setting up your dev environment.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -371,7 +405,7 @@ You would like to contribute to GtS? Great! ❤️❤️❤️ Check out the iss
|
||||||
|
|
||||||
For questions and comments, you can [join our Matrix space](https://matrix.to/#/#gotosocial-space:superseriousbusiness.org) at `#gotosocial-space:superseriousbusiness.org`. This is the quickest way to reach the devs. You can also mail [admin@gotosocial.org](mailto:admin@gotosocial.org).
|
For questions and comments, you can [join our Matrix space](https://matrix.to/#/#gotosocial-space:superseriousbusiness.org) at `#gotosocial-space:superseriousbusiness.org`. This is the quickest way to reach the devs. You can also mail [admin@gotosocial.org](mailto:admin@gotosocial.org).
|
||||||
|
|
||||||
For bugs and feature requests, please check to see if there's [already an issue](https://github.com/superseriousbusiness/gotosocial/issues), and if not, open one or use one of the above channels to make a request (if you don't have a Github account).
|
For bugs and feature requests, please check to see if there's [already an issue](https://codeberg.org/superseriousbusiness/gotosocial/issues), and if not, open one or use one of the above channels to make a request (if you don't have a Codeberg account).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -410,6 +444,7 @@ The following open source libraries, frameworks, and tools are used by GoToSocia
|
||||||
- [gruf/go-mutexes](https://codeberg.org/gruf/go-mutexes); safemutex & mutex map. [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); synchronization utilities. [MIT License](https://spdx.org/licenses/MIT.html).
|
- [gruf/go-runners](https://codeberg.org/gruf/go-runners); synchronization utilities. [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-sched](https://codeberg.org/gruf/go-sched); task scheduler. [MIT License](https://spdx.org/licenses/MIT.html).
|
||||||
|
- [gruf/go-split](https://codeberg.org/gruf/go-split); configuration string handling. [MIT License](https://spdx.org/licenses/MIT.html).
|
||||||
- [gruf/go-storage](https://codeberg.org/gruf/go-storage); file storage backend (local & s3). [MIT License](https://spdx.org/licenses/MIT.html).
|
- [gruf/go-storage](https://codeberg.org/gruf/go-storage); file storage backend (local & s3). [MIT License](https://spdx.org/licenses/MIT.html).
|
||||||
- [gruf/go-structr](https://codeberg.org/gruf/go-structr); struct caching + queueing with automated indexing by field. [MIT License](https://spdx.org/licenses/MIT.html).
|
- [gruf/go-structr](https://codeberg.org/gruf/go-structr); struct caching + queueing with automated indexing by field. [MIT License](https://spdx.org/licenses/MIT.html).
|
||||||
- jackc:
|
- jackc:
|
||||||
|
|
@ -426,15 +461,16 @@ The following open source libraries, frameworks, and tools are used by GoToSocia
|
||||||
- [mvdan.cc/xurls](https://github.com/mvdan/xurls); URL parsing regular expressions. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html).
|
- [mvdan.cc/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).
|
- [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).
|
- [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).
|
||||||
|
- [pquerna/otp](https://github.com/pquerna/otp); One Time Password utilities. [Apache-2.0 License](https://spdx.org/licenses/Apache-2.0.html).
|
||||||
- spf13:
|
- spf13:
|
||||||
- [spf13/cobra](https://github.com/spf13/cobra); command-line tooling. [Apache-2.0 License](https://spdx.org/licenses/Apache-2.0.html).
|
- [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).
|
- [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).
|
- [stretchr/testify](https://github.com/stretchr/testify); test framework. [MIT License](https://spdx.org/licenses/MIT.html).
|
||||||
- superseriousbusiness:
|
- 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/activity](https://code.superseriousbusiness.org/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://codeberg.org/superseriousbusiness/exif-terminator); EXIF data removal. [GNU AGPL v3 LICENSE](https://spdx.org/licenses/AGPL-3.0-or-later.html).
|
- [superseriousbusiness/exif-terminator](https://code.superseriousbusiness.org/exif-terminator); EXIF data removal. [GNU AGPL v3 LICENSE](https://spdx.org/licenses/AGPL-3.0-or-later.html).
|
||||||
- [superseriousbusiness/httpsig](https://github.com/superseriousbusiness/httpsig) forked from [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).
|
- [superseriousbusiness/httpsig](https://code.superseriousbusiness.org/httpsig) forked from [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).
|
||||||
- [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/oauth2](https://code.superseriousbusiness.org/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).
|
||||||
- [temoto/robotstxt](https://github.com/temoto/robotstxt); robots.txt parsing. [MIT License](https://spdx.org/licenses/MIT.html).
|
- [temoto/robotstxt](https://github.com/temoto/robotstxt); robots.txt parsing. [MIT License](https://spdx.org/licenses/MIT.html).
|
||||||
- [tdewolff/minify](https://github.com/tdewolff/minify); HTML minification for Markdown-submitted posts. [MIT License](https://spdx.org/licenses/MIT.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).
|
- [uber-go/automaxprocs](https://github.com/uber-go/automaxprocs); GOMAXPROCS automation. [MIT License](https://spdx.org/licenses/MIT.html).
|
||||||
|
|
@ -452,10 +488,10 @@ Sloth logo by [Anna Abramek](https://abramek.art/).
|
||||||
|
|
||||||
The Creative Commons Attribution-ShareAlike 4.0 International License license applies specifically to the following files and subdirectories of this repository:
|
The Creative Commons Attribution-ShareAlike 4.0 International License license applies specifically to the following files and subdirectories of this repository:
|
||||||
|
|
||||||
- [sloth logo png](https://github.com/superseriousbusiness/gotosocial/blob/main/web/assets/logo.png)
|
- [sloth logo png](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/web/assets/logo.png)
|
||||||
- [sloth logo webp](https://github.com/superseriousbusiness/gotosocial/blob/main/web/assets/logo.webp)
|
- [sloth logo webp](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/web/assets/logo.webp)
|
||||||
- [sloth logo svg](https://github.com/superseriousbusiness/gotosocial/blob/main/web/assets/logo.svg)
|
- [sloth logo svg](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/web/assets/logo.svg)
|
||||||
- [all default avatars](https://github.com/superseriousbusiness/gotosocial/blob/main/web/assets/default_avatars)
|
- [all default avatars](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/web/assets/default_avatars)
|
||||||
|
|
||||||
Under the terms of the license, you are free to:
|
Under the terms of the license, you are free to:
|
||||||
|
|
||||||
|
|
@ -513,7 +549,7 @@ If after reading this you are still interested in supporting us, that is wonderf
|
||||||
|
|
||||||
<img src="https://nlnet.nl/logo/NGI/NGIZero-green.hex.svg" width="75" alt="NGIZero logo"/>
|
<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 was 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).
|
Combined with the above crowdfunding sources, 2023 Alpha development of GoToSocial was 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://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/archive/nlnet/2022-next-generation-internet-zero.md).
|
||||||
|
|
||||||
2024 Beta development of GoToSocial is being funded by an additional 50,000 EUR grant from the [NGI0 Entrust Fund](https://nlnet.nl/entrust/), via [NLnet](https://nlnet.nl/).
|
2024 Beta development of GoToSocial is being funded by an additional 50,000 EUR grant from the [NGI0 Entrust Fund](https://nlnet.nl/entrust/), via [NLnet](https://nlnet.nl/).
|
||||||
|
|
||||||
|
|
@ -523,7 +559,7 @@ Combined with the above crowdfunding sources, 2023 Alpha development of GoToSoci
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
GoToSocial is free software, licensed under the [GNU AGPL v3 LICENSE](https://github.com/superseriousbusiness/gotosocial/blob/main/LICENSE). We encourage forking and changing the code, hacking around with it, and experimenting.
|
GoToSocial is free software, licensed under the [GNU AGPL v3 LICENSE](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/LICENSE). We encourage forking and changing the code, hacking around with it, and experimenting.
|
||||||
|
|
||||||
See [here](https://www.gnu.org/licenses/why-affero-gpl.html) for the differences between AGPL versus GPL licensing, and [here](https://www.gnu.org/licenses/gpl-faq.html) for FAQ's about GPL licenses, including the AGPL.
|
See [here](https://www.gnu.org/licenses/why-affero-gpl.html) for the differences between AGPL versus GPL licensing, and [here](https://www.gnu.org/licenses/gpl-faq.html) for FAQ's about GPL licenses, including the AGPL.
|
||||||
|
|
||||||
|
|
|
||||||
18
ROADMAP.md
18
ROADMAP.md
|
|
@ -13,10 +13,10 @@ Big thank you to all of our [Open Collective](https://opencollective.com/gotosoc
|
||||||
- [Beta Aims](#beta-aims)
|
- [Beta Aims](#beta-aims)
|
||||||
- [Timeline](#timeline)
|
- [Timeline](#timeline)
|
||||||
- [Mid 2023](#mid-2023)
|
- [Mid 2023](#mid-2023)
|
||||||
- [Mid/late 2023](#midlate-2023)
|
- [Mid/late 2023](#mid-late-2023)
|
||||||
- [Early 2024](#early-2024)
|
- [Early 2024](#early-2024)
|
||||||
- [BETA milestone](#beta-milestone)
|
- [BETA milestone](#beta-milestone)
|
||||||
- [Remainder 2024 - early 2025](#remainder-2024---early-2025)
|
- [Remainder 2024 - early 2025](#remainder-2024-early-2025)
|
||||||
- [On the way out of BETA to STABLE RELEASE](#on-the-way-out-of-beta-to-stable-release)
|
- [On the way out of BETA to STABLE RELEASE](#on-the-way-out-of-beta-to-stable-release)
|
||||||
- [Wishlist](#wishlist)
|
- [Wishlist](#wishlist)
|
||||||
|
|
||||||
|
|
@ -47,13 +47,13 @@ What follows is a rough timeline of features that will be implemented on the roa
|
||||||
|
|
||||||
### Mid 2023
|
### Mid 2023
|
||||||
|
|
||||||
- [x] **Hashtags** -- implement federating hashtags and viewing hashtags to allow users to discover posts that they might be interested in. (Done! https://github.com/superseriousbusiness/gotosocial/pull/2032).
|
- [x] **Hashtags** -- implement federating hashtags and viewing hashtags to allow users to discover posts that they might be interested in. (Done! https://codeberg.org/superseriousbusiness/gotosocial/pulls/2032).
|
||||||
|
|
||||||
### Mid/late 2023
|
### Mid/late 2023
|
||||||
|
|
||||||
- [x] **Polls** -- implementing parsing, creating, and voting in polls. (Done! https://github.com/superseriousbusiness/gotosocial/pull/2330)
|
- [x] **Polls** -- implementing parsing, creating, and voting in polls. (Done! https://codeberg.org/superseriousbusiness/gotosocial/pulls/2330)
|
||||||
- [x] **Mute posts/threads** -- opt-out of notifications for replies to a thread; no longer show a given post in your timeline. (Done! https://github.com/superseriousbusiness/gotosocial/pull/2278)
|
- [x] **Mute posts/threads** -- opt-out of notifications for replies to a thread; no longer show a given post in your timeline. (Done! https://codeberg.org/superseriousbusiness/gotosocial/pulls/2278)
|
||||||
- [x] **Limited peering/allowlists** -- allow instance admins to limit federation with other instances by default. (Done! https://github.com/superseriousbusiness/gotosocial/pull/2200)
|
- [x] **Limited peering/allowlists** -- allow instance admins to limit federation with other instances by default. (Done! https://codeberg.org/superseriousbusiness/gotosocial/pulls/2200)
|
||||||
|
|
||||||
### Early 2024
|
### Early 2024
|
||||||
|
|
||||||
|
|
@ -73,10 +73,10 @@ These are provided in no specific order.
|
||||||
- [x] **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.
|
- [x] **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.
|
||||||
- [x] **Block + allow list subscriptions** -- allow instance admins to subscribe their instance to domain block/allow lists.
|
- [x] **Block + allow list subscriptions** -- allow instance admins to subscribe their instance to domain block/allow lists.
|
||||||
- [x] **Direct conversation view** -- allow users to easily page through all direct-message conversations they're a part of.
|
- [x] **Direct conversation view** -- allow users to easily page through all direct-message conversations they're a part of.
|
||||||
- [ ] **Oauth token management** -- create / view / invalidate OAuth tokens via the settings panel.
|
- [x] **Oauth token management** -- create / view / invalidate OAuth tokens via the settings panel.
|
||||||
- [ ] **Status EDIT support** -- edit statuses that you've created, without having to delete + redraft. Federate edits out properly.
|
- [x] **Status EDIT support** -- edit statuses that you've created, without having to delete + redraft. Federate edits out properly.
|
||||||
- [ ] **Fediverse relay support** -- publish posts to relays, pull posts from relays.
|
- [ ] **Fediverse relay support** -- publish posts to relays, pull posts from relays.
|
||||||
- [ ] **Two factor authentication (2fa)** -- allow users to enable 2FA for their account via the settings panel, enforce 2FA on login.
|
- [x] **Two factor authentication (2fa)** -- allow users to enable 2FA for their account via the settings panel, enforce 2FA on login.
|
||||||
- [ ] **Moderation: Append content warning / mark-as-sensitive all content from an instance/account**.
|
- [ ] **Moderation: Append content warning / mark-as-sensitive all content from an instance/account**.
|
||||||
|
|
||||||
More tbd!
|
More tbd!
|
||||||
|
|
|
||||||
1
SECURITY.md
Normal file
1
SECURITY.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Please email security issues to: admin@gotosocial.org
|
||||||
|
|
@ -95,7 +95,7 @@ todo
|
||||||
|
|
||||||
### Friendica
|
### 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.
|
Unsure: Friendica and GoToSocial still don't federate properly with one another (https://codeberg.org/superseriousbusiness/gotosocial/issues/169) so it's hard to test this.
|
||||||
|
|
||||||
## What should GoToSocial do?
|
## What should GoToSocial do?
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ GoToSocial
|
||||||
|
|
||||||
> Website / wiki
|
> Website / wiki
|
||||||
|
|
||||||
https://github.com/superseriousbusiness/gotosocial / https://docs.gotosocial.org
|
https://codeberg.org/superseriousbusiness/gotosocial / https://docs.gotosocial.org
|
||||||
|
|
||||||
> Abstract: Can you explain the whole project and its expected outcome(s). (you have 1200 characters)
|
> Abstract: Can you explain the whole project and its expected outcome(s). (you have 1200 characters)
|
||||||
|
|
||||||
|
|
@ -87,8 +87,8 @@ Thirdly, we want to make GtS as customizable as possible by allowing admins to e
|
||||||
|
|
||||||
The main technical challenges we foresee on the project are:
|
The main technical challenges we foresee on the project are:
|
||||||
|
|
||||||
1. Ensuring compatibility with other AP servers (see here: https://github.com/superseriousbusiness/gotosocial/projects/4).
|
1. Ensuring compatibility with other AP servers (see federation issues: https://codeberg.org/superseriousbusiness/gotosocial/issues?labels=378188).
|
||||||
2. Ensuring compatibility with clients that use the Mastodon API (see here: https://github.com/superseriousbusiness/gotosocial/projects/5).
|
2. Ensuring compatibility with clients that use the Mastodon API (see client compatibility issues: https://codeberg.org/superseriousbusiness/gotosocial/issues?labels=378194).
|
||||||
3. Designing nuanced federation safety features that allow instance admins to screen federation without totally breaking it. This will require careful design discussions and lots of testing.
|
3. Designing nuanced federation safety features that allow instance admins to screen federation without totally breaking it. This will require careful design discussions and lots of testing.
|
||||||
4. Implementing our own open-source http signature library with a reference implementation of the latest draft of the http signature proposal: https://httpwg.org/http-extensions/draft-ietf-httpbis-message-signatures.html.
|
4. Implementing our own open-source http signature library with a reference implementation of the latest draft of the http signature proposal: https://httpwg.org/http-extensions/draft-ietf-httpbis-message-signatures.html.
|
||||||
5. Writing + maintaining our own extensions to the AP protocol (see below).
|
5. Writing + maintaining our own extensions to the AP protocol (see below).
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ GoToSocial
|
||||||
|
|
||||||
> Website / wiki
|
> Website / wiki
|
||||||
|
|
||||||
https://github.com/superseriousbusiness/gotosocial / https://docs.gotosocial.org
|
https://codeberg.org/superseriousbusiness/gotosocial / https://docs.gotosocial.org
|
||||||
|
|
||||||
> Abstract: Can you explain the whole project and its expected outcome(s). (you have 1200 characters)
|
> Abstract: Can you explain the whole project and its expected outcome(s). (you have 1200 characters)
|
||||||
|
|
||||||
|
|
@ -20,7 +20,7 @@ GtS emphasizes user safety and privacy. Unlike other AP servers, it always requi
|
||||||
|
|
||||||
GtS values ease of deployment and maintenance; this means low system requirements, simple configuration, minimal external dependencies, and clear documentation. GtS makes it easy + affordable for self-hosting newcomers to set up a Fediverse server on low- (or even solar-) powered equipment they might have lying around at home.
|
GtS values ease of deployment and maintenance; this means low system requirements, simple configuration, minimal external dependencies, and clear documentation. GtS makes it easy + affordable for self-hosting newcomers to set up a Fediverse server on low- (or even solar-) powered equipment they might have lying around at home.
|
||||||
|
|
||||||
GtS began development in Feb 2021. It is still in Alpha, and we hope to use NLNet funding to bring it up to the Beta phase. The project roadmap (https://github.com/superseriousbusiness/gotosocial/blob/main/ROADMAP.md) gives more information on what we have planned.
|
GtS began development in Feb 2021. It is still in Alpha, and we hope to use NLNet funding to bring it up to the Beta phase. The project roadmap (https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/ROADMAP.md) gives more information on what we have planned.
|
||||||
|
|
||||||
> Have you been involved with projects or organisations relevant to this project before? And if so, can you tell us a bit about your contributions? (Optional) This can help us determine if you are the right person to undertake this effort.
|
> Have you been involved with projects or organisations relevant to this project before? And if so, can you tell us a bit about your contributions? (Optional) This can help us determine if you are the right person to undertake this effort.
|
||||||
|
|
||||||
|
|
@ -45,7 +45,7 @@ Aside from GoToSocial, I've also made small PRs upstream to the ActivityPub libr
|
||||||
|
|
||||||
Currently, GoToSocial receives about €22/week from LiberaPay donations - https://liberapay.com/gotosocial. I have been paying my own costs for working on the project from my savings, which is unfortunately not sustainable for a lot longer.
|
Currently, GoToSocial receives about €22/week from LiberaPay donations - https://liberapay.com/gotosocial. I have been paying my own costs for working on the project from my savings, which is unfortunately not sustainable for a lot longer.
|
||||||
|
|
||||||
The requested NLNet budget will be used to fund the remaining Alpha portion of development, and bring GoToSocial into the Beta phase (see the roadmap - https://github.com/superseriousbusiness/gotosocial/blob/main/ROADMAP.md). In practical terms, this means paying myself to work full time on the project for one year, and paying for contributions from other developers as well.
|
The requested NLNet budget will be used to fund the remaining Alpha portion of development, and bring GoToSocial into the Beta phase (see the roadmap - https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/ROADMAP.md). In practical terms, this means paying myself to work full time on the project for one year, and paying for contributions from other developers as well.
|
||||||
|
|
||||||
To pay my living costs + rent I need to make about €2,000/month after tax, working full time. In Belgium, that equates to about €3,000/month, which is €36,000 for one year of work. Naively calculated at 40 hours / week, that's €18.75 per hour.
|
To pay my living costs + rent I need to make about €2,000/month after tax, working full time. In Belgium, that equates to about €3,000/month, which is €36,000 for one year of work. Naively calculated at 40 hours / week, that's €18.75 per hour.
|
||||||
|
|
||||||
|
|
@ -83,8 +83,8 @@ Thirdly, we want to make GtS as customizable as possible by allowing admins to e
|
||||||
|
|
||||||
The main technical challenges we foresee on the project are:
|
The main technical challenges we foresee on the project are:
|
||||||
|
|
||||||
1. Ensuring compatibility with other AP servers (see here: https://github.com/superseriousbusiness/gotosocial/projects/4).
|
1. Ensuring compatibility with other AP servers (see federation issues: https://codeberg.org/superseriousbusiness/gotosocial/issues?labels=378188).
|
||||||
2. Ensuring compatibility with clients that use the Mastodon API (see here: https://github.com/superseriousbusiness/gotosocial/projects/5).
|
2. Ensuring compatibility with clients that use the Mastodon API (see client compatibility issues: https://codeberg.org/superseriousbusiness/gotosocial/issues?labels=378194).
|
||||||
3. Designing nuanced federation safety features that allow instance admins to screen federation without totally breaking it. This will require careful design discussions and lots of testing.
|
3. Designing nuanced federation safety features that allow instance admins to screen federation without totally breaking it. This will require careful design discussions and lots of testing.
|
||||||
4. Implementing our own open-source http signature library with a reference implementation of the latest draft of the http signature proposal: https://httpwg.org/http-extensions/draft-ietf-httpbis-message-signatures.html.
|
4. Implementing our own open-source http signature library with a reference implementation of the latest draft of the http signature proposal: https://httpwg.org/http-extensions/draft-ietf-httpbis-message-signatures.html.
|
||||||
5. Writing + maintaining our own extensions to the AP protocol (see below).
|
5. Writing + maintaining our own extensions to the AP protocol (see below).
|
||||||
|
|
|
||||||
123
archive/nlnet/2025-ngi-zero-commons-fund.md
Normal file
123
archive/nlnet/2025-ngi-zero-commons-fund.md
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
# NLnet Grant Application - NGI Zero Commons 2025
|
||||||
|
|
||||||
|
This document is the application on behalf of GoToSocial for funding from the [NLnet](https://nlnet.nl) [NGI Zero Commons fund](https://nlnet.nl/commonsfund/), April 2025. See [here](https://nlnet.nl/propose/).
|
||||||
|
|
||||||
|
## General Project Information
|
||||||
|
|
||||||
|
> Project Name
|
||||||
|
|
||||||
|
GoToSocial
|
||||||
|
|
||||||
|
> Website / wiki
|
||||||
|
|
||||||
|
https://codeberg.org/superseriousbusiness/gotosocial / https://docs.gotosocial.org
|
||||||
|
|
||||||
|
> Abstract: Can you explain the whole project and its expected outcome(s). (you have 1200 characters)
|
||||||
|
|
||||||
|
GoToSocial (GtS) is an ActivityPub social network server, which provides a lightweight, simple entryway into Fediverse hosting. It is comparable to (but distinct from) projects such as Mastodon, Friendica, and PixelFed.
|
||||||
|
|
||||||
|
GtS emphasizes user safety and privacy. Unlike other AP servers, it always requires http signatures, and makes a strong differentiation between 'public' and other kinds of posts. It also makes it very easy for admins to block instances they don't want to interact with, by allowing them to subscribe to block lists or allow lists, and to import blocks, ensuring that users stay safe.
|
||||||
|
|
||||||
|
GtS values ease of deployment and maintenance; this means low system requirements, simple configuration, minimal external dependencies, and clear documentation. GtS makes it easy + affordable for self-hosting newcomers to set up a Fediverse server on low- (or even solar-) powered equipment they might have lying around at home.
|
||||||
|
|
||||||
|
GtS began development in Feb 2021, and entered Beta in 2024. We hope to use NLnet funding to continue tuning performance and adding features as we work towards a 1.0 release.
|
||||||
|
|
||||||
|
> Have you been involved with projects or organisations relevant to this project before? And if so, can you tell us a bit about your contributions? (Optional) This can help us determine if you are the right person to undertake this effort. (max 2500 characters)
|
||||||
|
|
||||||
|
I have been working on GoToSocial since the beginning of the project, first independently, paying myself from my savings, and then thanks to two previous grants from NLnet. My colleague Kim has a similar trajectory, as they now work full time on the project, again thanks to NLnet.
|
||||||
|
|
||||||
|
Over the last years we have put many thousands of hours of work into the project: writing code and documentation, fixing bugs, communicating with contributors and doing code review, deploying infrastructure for project builds and discussion, doing project planning, and answering user questions.
|
||||||
|
|
||||||
|
Aside from our work on GoToSocial, we also maintain a fork of the Activity library (https://codeberg.org/superseriousbusiness/activity), a fork of the standalone Mastodon frontend customized for GoToSocial (https://codeberg.org/superseriousbusiness/masto-fe-standalone, deployed at https://masto-fe.superseriousbusiness.org), and Kim in particular maintains a large amount of libraries used by the project (https://codeberg.org/gruf), particularly go-ffmpreg (https://codeberg.org/gruf/go-ffmpreg).
|
||||||
|
|
||||||
|
## Requested Support
|
||||||
|
|
||||||
|
> Explain what the requested budget will be used for?
|
||||||
|
> Does the project have other funding sources, both past and present?
|
||||||
|
> (If you want, you can in addition attach a budget at the bottom of the form)
|
||||||
|
> Explain costs for hardware, human labor (including rates used), travel cost to technical meetings, etc. (max 2500 characters, be concise)
|
||||||
|
|
||||||
|
GoToSocial has received two NLnet grants previously, one in 2022-2023, for 50k euros, and another one in 2024-2025, for 75k euros, which we shared between the two of us. These grants went towards paying living costs for myself and Kim (rent, groceries, utilities, taxes, etc), while we both worked full time on the project.
|
||||||
|
|
||||||
|
For our first grant we underbudgeted our own costs and ended up underpaying ourselves. We better estimated the second grant, but still had some issues doing more work than we budgeted for, taking account of bug fixes, extra features, and release coordination.
|
||||||
|
|
||||||
|
With the benefit of experience, this time we intend to budget more sensibly so that we don't end up going into our overdrafts before the end of each milestone, hence we are asking for a larger amount of funding: 100k euros in total, or 50k euros each per year. This amount of money is comparable to the rate for a mid/senior-level developer in the countries we live in, and should allow us to pay our costs without panic.
|
||||||
|
|
||||||
|
Happily, we receive a decent amount of money per month via OpenCollective (https://opencollective.com/gotosocial), which allows us to pay for costs (Greenhost.net) for our CI/CD + snapshot distribution server. It also allows us to pay our freelance colleague f0x for the contributions they are able to make when school is not too busy. As such, NLnet money does not need to be used for anything besides mine and Kim's living costs.
|
||||||
|
|
||||||
|
This year, we would like to use the NGI Zero Commons grant to fund development of the following efforts (more tbd):
|
||||||
|
|
||||||
|
- Add functionality to allow users and admins to configure cleanup of old statuses and accounts from the database, to keep database sizes smaller.
|
||||||
|
- Implement better threading support when statuses are deleted (ie., store + show status tombstones).
|
||||||
|
- Improve search performance and add full-text-search (SQLite, Postgres).
|
||||||
|
- Add additional in-memory caches for frontend object types (statuses, notifications, etc) to reduce database calls and improve response times.
|
||||||
|
- Add support for subscribing to relays, and allowing GoToSocial itself to act as a relay, improving connectivity with other instances.
|
||||||
|
- Add additional federation controls (silence/mute/limit instances).
|
||||||
|
- Add an opt-in profile directory to make it easier to discover accounts on an instance.
|
||||||
|
- Implement admin panel section to track unreachable instances, so that admins can tell whether another instance has shut down, and take appropriate action.
|
||||||
|
|
||||||
|
> Compare your own project with existing or historical efforts
|
||||||
|
> E.g. what is new, more thorough or otherwise different. (max 4000 characters, be concise)
|
||||||
|
|
||||||
|
ActivityPub is now a popular protocol, with a proliferation of AP implementations like Mastodon, Akkoma, Lemmy, Misskey (and its many forks), Pixelfed, WriteFreely, Wordpress, and more, each with its own focus and purpose. As strong as many of these implementations are, however, they are not without their downsides.
|
||||||
|
|
||||||
|
For instance, Mastodon and Pixelfed both lean more towards replicating existing corporate social media efforts (Twitter and Instagram respectively), which is offputting for many users. These two implementations are also complex to install and maintain, which puts many would-be admins off hosting their own servers, and leads to a concentration of users on developer-run servers like mastodon.social and pixelfed.social, which breaks the promise of decentralization.
|
||||||
|
|
||||||
|
Other ActivityPub-enabled microblogging softwares like Honk and Snac2 have fewer moving parts and lower system requirements, which has led to a surge of deployments of Snac2 in particular. However, their implementation of the Mastodon client API (the de-facto client API of the fediverse, for better or worse), misses some features, and the barebones admin functionality is not user-friendly for people unaccustomed to using the command line.
|
||||||
|
|
||||||
|
In addition, some other fediverse projects have a heavy front-end UI which results in a poor experience on low-bandwidth connections or low-powered devices.
|
||||||
|
|
||||||
|
GoToSocial's continued aim is to strike a balance between featureful but fairly resource-intensive AP implementations like Mastodon, and lighter and simpler AP implementations like Snac2 and Honk.
|
||||||
|
|
||||||
|
Firstly, GtS is small, easy to run, and very well documented. It uses about 200-300MiB of memory on average, so it can be deployed on tiny VPSs for cheap, and on single-board computers. It has no dependencies on things like ffmpeg or on-system SQLite, as everything is virtualized inside the single binary using WASM/Wazero. This means that people who want to run GoToSocial don't need to install or maintain anything else on the host, and it is very easy for third parties like Yunohost to package and distribute.
|
||||||
|
|
||||||
|
Secondly, the admin/settings panel offers admins and users the ability to easily customize their profiles and adjust the way that their instance looks, feels, and federates, and to handle day-to-day admin tasks like reviewing reports, blocking or allowing other instances, etc. The web view of profiles is rendered in simple HTML + CSS; Javascript is not required, but is used for progressive enhancement if available. This makes it relatively easy to view a GoToSocial profile on a mobile device with poor connection, on a lower-end laptop etc.
|
||||||
|
|
||||||
|
Thirdly, it provides strong safety features thanks to strict block implementation, always-on HTTP signature authentication, interaction controls, and allowing users to choose what visibility of level of posts can be viewed on their profile. The allowlist subscription functionality we added in 2024 is also critical here, as it allows groups of instances to easily federate only with each other, forming "islands" that can be more easily moderated than fully open federation.
|
||||||
|
|
||||||
|
Fourthly, we have been -- and continue to be -- fastidious with our Mastodon client API implementation, which means that GoToSocial can be used with a wide variety of clients that provide different experiences to the user. It even works with apps for Pixelfed, so user's can use a GoToSocial account for 'gram-style media-only posting.
|
||||||
|
|
||||||
|
> What are significant technical challenges you expect to solve during the project, if any? (optional but recommended, max 5000 characters)
|
||||||
|
|
||||||
|
Many of the technical challenges we expect to overcome during this development period are specific to the development efforts we will undertake as part of this grant:
|
||||||
|
|
||||||
|
- Status cleanup: Ensure that cleanup processes are capable of being regularly scheduled asynchronously, while not consuming all available server resources (with what will likely involve scanning the entirety of our largest database table!). This may require some clever indexing and/or marking of statuses as expirable in advance.
|
||||||
|
- Status tombstones: figure out whether we can write a migration to retroactively rethread old statuses that have become unlinked due to deletes.
|
||||||
|
- Relay support: figure out whether adding support for relay mode will require a separate relay binary, and if so, refactor sections of the codebase into libraries that can be shared by that binary.
|
||||||
|
- Relay support: make sure GoToSocial can subscribe to both existing relay services, as well as GoToSocial relays (this will probably involve lots of dipping into the codebases of existing relay services like fedi buzz, to figure out what they're doing). And vice versa: ensure that existing fedi implementations with relay support can subscribe to GoToSocial relays.
|
||||||
|
- Relay support: make sure that DB sizes and memory usage don't become too burdensome given the amount of statuses that relays are likely to process compared to a vanilla instance.
|
||||||
|
- Profile directory: ensure "discoverable" flag is respected; optimize required new database queries to ensure they use existing indexes (or figure out which new ones need to be created).
|
||||||
|
- Search support: ensure that the same functionality and performance is offered by both Postgres and SQLite; possibly refactor our database wrappers and migration code for this.
|
||||||
|
- Unreachable instances: develop a reasonable heuristic to determine whether an instance is unreachable; work out the best way of storing this information in the database and presenting it to admins via the settings panel.
|
||||||
|
|
||||||
|
Other technical challenges we will (continue to) address in the near future are not related to any specific milestone task:
|
||||||
|
|
||||||
|
- Ensure continued compatibility of GoToSocial with other fedi software projects.
|
||||||
|
- Ensure continued compatibility of GoToSocial with Mastodon API client apps.
|
||||||
|
- Refactor old parts of the codebase to increase readability and remove bugs.
|
||||||
|
- Make performance tweaks to the codebase wherever necessary (reduce CPU usage, improve memory usage).
|
||||||
|
- Increase coverage of our test suites.
|
||||||
|
- Continue to support seamless database migration from old versions of GoToSocial to newer ones.
|
||||||
|
- Refactor some of the frontend settings panel code to maximize code reuse and minimize compiled javascript size, before adding lots of new functionality.
|
||||||
|
- Move our CI/CD infrastructure and code repository from Github to Codeberg with minimal disruption to our work.
|
||||||
|
|
||||||
|
> Describe the ecosystem of the project, and how you will engage with relevant actors and promote the outcomes. E.g. which actors will you involve? Who should run or deploy your solution to make it a success? (max 2500 characters, be concise)
|
||||||
|
|
||||||
|
Much of the work we do involves debugging and solving interoperability issues with other federated softwares, which requires keeping communication channels open with the maintainers of those, and figuring out who needs to change what in order for the issue to be resolved. We've done that a lot over the last year or so:
|
||||||
|
|
||||||
|
- Fixed interop with Bandwagon: https://github.com/EmissarySocial/bandwagon/issues/152
|
||||||
|
- Fixed interop with Iceshrimp: https://codeberg.org/superseriousbusiness/gotosocial/issues/1947
|
||||||
|
- Coordinated interop with Mastodon: https://codeberg.org/superseriousbusiness/gotosocial/pulls/3703
|
||||||
|
- Fixed federation with Gancio: https://codeberg.org/superseriousbusiness/gotosocial/issues/3875
|
||||||
|
- Alerted Pixelfed of AP serialization issues: https://github.com/pixelfed/pixelfed/issues/5642
|
||||||
|
- Cajoled Bluesky into adding user-agent headers: https://github.com/bluesky-social/atproto/issues/3504
|
||||||
|
- Help out Writefreely with http signature request issues: https://github.com/writefreely/writefreely/issues/661#issuecomment-1951367449
|
||||||
|
- Debug federation with Lemmy along with one of the Lemmy devs: https://codeberg.org/superseriousbusiness/gotosocial/issues/2697
|
||||||
|
|
||||||
|
For GoToSocial-specific extensions to ActivityPub, we've also diligently documented what we've done so far, and exposed a GoToSocial namespace so that remote softwares can easily incorporate GtS extensions if they want to: https://docs.gotosocial.org/en/latest/federation/interaction_policy/, https://gotosocial.org/ns.
|
||||||
|
|
||||||
|
This is all part and parcel of our goal for GoToSocial to be a "good citizen" in terms of how it federates, and how we fit into the larger ActivityPub microblogging ecosystem. We intend to keep doing this!
|
||||||
|
|
||||||
|
## Attachments
|
||||||
|
|
||||||
|
> Attachments: add any additional information about the project that may help us to gain more insight into the proposed effort, for instance a more detailed task description, a justification of costs or relevant endorsements. Attachments should only contain background information, please make sure that the proposal without attachments is self-contained and concise. Don't waste too much time on this. Really.
|
||||||
|
|
@ -17,6 +17,6 @@
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "github.com/superseriousbusiness/gotosocial/internal/id"
|
import "code.superseriousbusiness.org/gotosocial/internal/id"
|
||||||
|
|
||||||
func main() { println(id.NewULID()) }
|
func main() { println(id.NewULID()) }
|
||||||
|
|
|
||||||
|
|
@ -24,21 +24,36 @@ import (
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
|
"code.superseriousbusiness.org/gotosocial/internal/db/bundb"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"code.superseriousbusiness.org/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
userprocessor "code.superseriousbusiness.org/gotosocial/internal/processing/user"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
"code.superseriousbusiness.org/gotosocial/internal/state"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/validate"
|
"code.superseriousbusiness.org/gotosocial/internal/util"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/validate"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// check function conformance
|
||||||
|
_ action.GTSAction = Create
|
||||||
|
_ action.GTSAction = List
|
||||||
|
_ action.GTSAction = Confirm
|
||||||
|
_ action.GTSAction = Promote
|
||||||
|
_ action.GTSAction = Demote
|
||||||
|
_ action.GTSAction = Enable
|
||||||
|
_ action.GTSAction = Disable
|
||||||
|
_ action.GTSAction = Password
|
||||||
|
)
|
||||||
|
|
||||||
func initState(ctx context.Context) (*state.State, error) {
|
func initState(ctx context.Context) (*state.State, error) {
|
||||||
var state state.State
|
var state state.State
|
||||||
state.Caches.Init()
|
state.Caches.Init()
|
||||||
state.Caches.Start()
|
if err := state.Caches.Start(); err != nil {
|
||||||
|
return nil, fmt.Errorf("error starting caches: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Only set state DB connection.
|
// Only set state DB connection.
|
||||||
// Don't need Actions or Workers for this (yet).
|
// Don't need Actions or Workers for this (yet).
|
||||||
|
|
@ -59,7 +74,7 @@ func stopState(state *state.State) error {
|
||||||
|
|
||||||
// Create creates a new account and user
|
// Create creates a new account and user
|
||||||
// in the database using the provided flags.
|
// in the database using the provided flags.
|
||||||
var Create action.GTSAction = func(ctx context.Context) error {
|
func Create(ctx context.Context) error {
|
||||||
state, err := initState(ctx)
|
state, err := initState(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -116,7 +131,7 @@ var Create action.GTSAction = func(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// List returns all existing local accounts.
|
// List returns all existing local accounts.
|
||||||
var List action.GTSAction = func(ctx context.Context) error {
|
func List(ctx context.Context) error {
|
||||||
state, err := initState(ctx)
|
state, err := initState(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -154,7 +169,7 @@ var List action.GTSAction = func(ctx context.Context) error {
|
||||||
|
|
||||||
// Confirm sets a user to Approved, sets Email to the current
|
// Confirm sets a user to Approved, sets Email to the current
|
||||||
// UnconfirmedEmail value, and sets ConfirmedAt to now.
|
// UnconfirmedEmail value, and sets ConfirmedAt to now.
|
||||||
var Confirm action.GTSAction = func(ctx context.Context) error {
|
func Confirm(ctx context.Context) error {
|
||||||
state, err := initState(ctx)
|
state, err := initState(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -196,7 +211,7 @@ var Confirm action.GTSAction = func(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Promote sets admin + moderator flags on a user to true.
|
// Promote sets admin + moderator flags on a user to true.
|
||||||
var Promote action.GTSAction = func(ctx context.Context) error {
|
func Promote(ctx context.Context) error {
|
||||||
state, err := initState(ctx)
|
state, err := initState(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -233,7 +248,7 @@ var Promote action.GTSAction = func(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Demote sets admin + moderator flags on a user to false.
|
// Demote sets admin + moderator flags on a user to false.
|
||||||
var Demote action.GTSAction = func(ctx context.Context) error {
|
func Demote(ctx context.Context) error {
|
||||||
state, err := initState(ctx)
|
state, err := initState(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -270,7 +285,7 @@ var Demote action.GTSAction = func(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable sets Disabled to true on a user.
|
// Disable sets Disabled to true on a user.
|
||||||
var Disable action.GTSAction = func(ctx context.Context) error {
|
func Disable(ctx context.Context) error {
|
||||||
state, err := initState(ctx)
|
state, err := initState(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -306,7 +321,7 @@ var Disable action.GTSAction = func(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable sets Disabled to false on a user.
|
// Enable sets Disabled to false on a user.
|
||||||
var Enable action.GTSAction = func(ctx context.Context) error {
|
func Enable(ctx context.Context) error {
|
||||||
state, err := initState(ctx)
|
state, err := initState(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -342,7 +357,7 @@ var Enable action.GTSAction = func(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Password sets the password of target account.
|
// Password sets the password of target account.
|
||||||
var Password action.GTSAction = func(ctx context.Context) error {
|
func Password(ctx context.Context) error {
|
||||||
state, err := initState(ctx)
|
state, err := initState(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -381,8 +396,47 @@ var Password action.GTSAction = func(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
user.EncryptedPassword = string(encryptedPassword)
|
user.EncryptedPassword = string(encryptedPassword)
|
||||||
|
log.Info(ctx, "Updating password; you must restart the server to use the new password.")
|
||||||
return state.DB.UpdateUser(
|
return state.DB.UpdateUser(
|
||||||
ctx, user,
|
ctx, user,
|
||||||
"encrypted_password",
|
"encrypted_password",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disable2FA disables 2FA for target account.
|
||||||
|
func Disable2FA(ctx context.Context) error {
|
||||||
|
state, err := initState(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
// Ensure state gets stopped on return.
|
||||||
|
if err := stopState(state); err != nil {
|
||||||
|
log.Error(ctx, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
username := config.GetAdminAccountUsername()
|
||||||
|
if err := validate.Username(username); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
account, err := state.DB.GetAccountByUsernameDomain(ctx, username, "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := state.DB.GetUserByAccountID(ctx, account.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = userprocessor.TwoFactorDisable(ctx, state, user)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("2fa disabled\n")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,95 +18,181 @@
|
||||||
package media
|
package media
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"code.superseriousbusiness.org/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
|
"code.superseriousbusiness.org/gotosocial/internal/db/bundb"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"code.superseriousbusiness.org/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/paging"
|
"code.superseriousbusiness.org/gotosocial/internal/paging"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
"code.superseriousbusiness.org/gotosocial/internal/state"
|
||||||
|
"codeberg.org/gruf/go-byteutil"
|
||||||
|
"codeberg.org/gruf/go-fastpath/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// check function conformance.
|
||||||
|
var _ action.GTSAction = ListAttachments
|
||||||
|
var _ action.GTSAction = ListEmojis
|
||||||
|
|
||||||
|
// ListAttachments lists local, remote, or all attachment paths.
|
||||||
|
func ListAttachments(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(); err != nil {
|
||||||
|
log.Errorf(ctx, "error shutting down: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// List attachment media paths from db.
|
||||||
|
return list.ListAttachmentPaths(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListEmojis lists local, remote, or all emoji filepaths.
|
||||||
|
func ListEmojis(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(); err != nil {
|
||||||
|
log.Errorf(ctx, "error shutting down: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// List emoji media paths from db.
|
||||||
|
return list.ListEmojiPaths(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
type list struct {
|
type list struct {
|
||||||
dbService db.DB
|
|
||||||
state *state.State
|
state *state.State
|
||||||
page paging.Page
|
|
||||||
localOnly bool
|
localOnly bool
|
||||||
remoteOnly bool
|
remoteOnly bool
|
||||||
out *bufio.Writer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a list of attachment using a custom filter
|
func (l *list) ListAttachmentPaths(ctx context.Context) error {
|
||||||
func (l *list) GetAllAttachmentPaths(ctx context.Context, filter func(*gtsmodel.MediaAttachment) string) ([]string, error) {
|
// Page reused for iterative
|
||||||
res := make([]string, 0, 100)
|
// attachment queries, with
|
||||||
|
// predefined limit.
|
||||||
|
var page paging.Page
|
||||||
|
page.Limit = 500
|
||||||
|
|
||||||
|
// Storage base path, used for path building.
|
||||||
|
basePath := config.GetStorageLocalBasePath()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// Get the next page of media attachments up to max ID.
|
// Get next page of media attachments up to max ID.
|
||||||
attachments, err := l.dbService.GetAttachments(ctx, &l.page)
|
medias, err := l.state.DB.GetAttachments(ctx, &page)
|
||||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||||
return nil, fmt.Errorf("failed to retrieve media metadata from database: %w", err)
|
return fmt.Errorf("failed to fetch media from database: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get current max ID.
|
// Get current max ID.
|
||||||
maxID := l.page.Max.Value
|
maxID := page.Max.Value
|
||||||
|
|
||||||
// If no attachments or the same group is returned, we reached the end.
|
// If no media or the same group is returned, we reached end.
|
||||||
if len(attachments) == 0 || maxID == attachments[len(attachments)-1].ID {
|
if len(medias) == 0 || maxID == medias[len(medias)-1].ID {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use last ID as the next 'maxID' value.
|
// Use last ID as the next 'maxID'.
|
||||||
maxID = attachments[len(attachments)-1].ID
|
maxID = medias[len(medias)-1].ID
|
||||||
l.page.Max = paging.MaxID(maxID)
|
page.Max.Value = maxID
|
||||||
|
|
||||||
for _, a := range attachments {
|
switch {
|
||||||
v := filter(a)
|
case l.localOnly:
|
||||||
if v != "" {
|
// Only print local media paths.
|
||||||
res = append(res, v)
|
for _, media := range medias {
|
||||||
|
if media.RemoteURL == "" {
|
||||||
|
printMediaPaths(basePath, media)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case l.remoteOnly:
|
||||||
|
// Only print remote media paths.
|
||||||
|
for _, media := range medias {
|
||||||
|
if media.RemoteURL != "" {
|
||||||
|
printMediaPaths(basePath, media)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Print all known media paths.
|
||||||
|
for _, media := range medias {
|
||||||
|
printMediaPaths(basePath, media)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res, nil
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a list of emojis using a custom filter
|
func (l *list) ListEmojiPaths(ctx context.Context) error {
|
||||||
func (l *list) GetAllEmojisPaths(ctx context.Context, filter func(*gtsmodel.Emoji) string) ([]string, error) {
|
// Page reused for iterative
|
||||||
res := make([]string, 0, 100)
|
// attachment queries, with
|
||||||
|
// predefined limit.
|
||||||
|
var page paging.Page
|
||||||
|
page.Limit = 500
|
||||||
|
|
||||||
|
// Storage base path, used for path building.
|
||||||
|
basePath := config.GetStorageLocalBasePath()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// Get the next page of emoji media up to max ID.
|
// Get the next page of emoji media up to max ID.
|
||||||
attachments, err := l.dbService.GetEmojis(ctx, &l.page)
|
emojis, err := l.state.DB.GetEmojis(ctx, &page)
|
||||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||||
return nil, fmt.Errorf("failed to retrieve media metadata from database: %w", err)
|
return fmt.Errorf("failed to fetch emojis from database: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get current max ID.
|
// Get current max ID.
|
||||||
maxID := l.page.Max.Value
|
maxID := page.Max.Value
|
||||||
|
|
||||||
// If no attachments or the same group is returned, we reached the end.
|
// If no emojis or the same group is returned, we reached end.
|
||||||
if len(attachments) == 0 || maxID == attachments[len(attachments)-1].ID {
|
if len(emojis) == 0 || maxID == emojis[len(emojis)-1].ID {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use last ID as the next 'maxID' value.
|
// Use last ID as the next 'maxID'.
|
||||||
maxID = attachments[len(attachments)-1].ID
|
maxID = emojis[len(emojis)-1].ID
|
||||||
l.page.Max = paging.MaxID(maxID)
|
page.Max.Value = maxID
|
||||||
|
|
||||||
for _, a := range attachments {
|
switch {
|
||||||
v := filter(a)
|
case l.localOnly:
|
||||||
if v != "" {
|
// Only print local emoji paths.
|
||||||
res = append(res, v)
|
for _, emoji := range emojis {
|
||||||
|
if emoji.ImageRemoteURL == "" {
|
||||||
|
printEmojiPaths(basePath, emoji)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case l.remoteOnly:
|
||||||
|
// Only print remote emoji paths.
|
||||||
|
for _, emoji := range emojis {
|
||||||
|
if emoji.ImageRemoteURL != "" {
|
||||||
|
printEmojiPaths(basePath, emoji)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Print all known emoji paths.
|
||||||
|
for _, emoji := range emojis {
|
||||||
|
printEmojiPaths(basePath, emoji)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res, nil
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupList(ctx context.Context) (*list, error) {
|
func setupList(ctx context.Context) (*list, error) {
|
||||||
|
|
@ -124,148 +210,84 @@ func setupList(ctx context.Context) (*list, error) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize caches.
|
||||||
state.Caches.Init()
|
state.Caches.Init()
|
||||||
state.Caches.Start()
|
|
||||||
|
// Ensure background cache tasks are running.
|
||||||
|
if err := state.Caches.Start(); err != nil {
|
||||||
|
return nil, fmt.Errorf("error starting caches: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
// Only set state DB connection.
|
// Only set state DB connection.
|
||||||
// Don't need Actions or Workers for this.
|
// Don't need Actions or Workers for this.
|
||||||
dbService, err := bundb.NewBunDBService(ctx, &state)
|
state.DB, err = bundb.NewBunDBService(ctx, &state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error creating dbservice: %w", err)
|
return nil, fmt.Errorf("error creating dbservice: %w", err)
|
||||||
}
|
}
|
||||||
state.DB = dbService
|
|
||||||
|
|
||||||
return &list{
|
return &list{
|
||||||
dbService: dbService,
|
|
||||||
state: &state,
|
state: &state,
|
||||||
page: paging.Page{Limit: 200},
|
|
||||||
localOnly: localOnly,
|
localOnly: localOnly,
|
||||||
remoteOnly: remoteOnly,
|
remoteOnly: remoteOnly,
|
||||||
out: bufio.NewWriter(os.Stdout),
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *list) shutdown() error {
|
func (l *list) shutdown() error {
|
||||||
l.out.Flush()
|
err := l.state.DB.Close()
|
||||||
err := l.dbService.Close()
|
|
||||||
l.state.Caches.Stop()
|
l.state.Caches.Stop()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListAttachments lists local, remote, or all attachment paths.
|
// reusable path building buffer,
|
||||||
var ListAttachments action.GTSAction = func(ctx context.Context) error {
|
// only usable here as we're not
|
||||||
list, err := setupList(ctx)
|
// performing concurrent writes.
|
||||||
if err != nil {
|
var pb fastpath.Builder
|
||||||
return err
|
|
||||||
|
// reusable string output buffer,
|
||||||
|
// only usable here as we're not
|
||||||
|
// performing concurrent writes.
|
||||||
|
var outbuf byteutil.Buffer
|
||||||
|
|
||||||
|
func printMediaPaths(basePath string, media *gtsmodel.MediaAttachment) {
|
||||||
|
// Append file path if present.
|
||||||
|
if media.File.Path != "" {
|
||||||
|
path := pb.Join(basePath, media.File.Path)
|
||||||
|
_, _ = outbuf.WriteString(path + "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
// Append thumb path if present.
|
||||||
// Ensure lister gets shutdown on exit.
|
if media.Thumbnail.Path != "" {
|
||||||
if err := list.shutdown(); err != nil {
|
path := pb.Join(basePath, media.Thumbnail.Path)
|
||||||
log.Error(ctx, err)
|
_, _ = outbuf.WriteString(path + "\n")
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
var (
|
|
||||||
mediaPath = config.GetStorageLocalBasePath()
|
|
||||||
filter func(*gtsmodel.MediaAttachment) string
|
|
||||||
)
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case list.localOnly:
|
|
||||||
filter = func(m *gtsmodel.MediaAttachment) string {
|
|
||||||
if m.RemoteURL != "" {
|
|
||||||
// Remote, not
|
|
||||||
// interested.
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return path.Join(mediaPath, m.File.Path)
|
|
||||||
}
|
|
||||||
|
|
||||||
case list.remoteOnly:
|
|
||||||
filter = func(m *gtsmodel.MediaAttachment) string {
|
|
||||||
if m.RemoteURL == "" {
|
|
||||||
// Local, not
|
|
||||||
// interested.
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return path.Join(mediaPath, m.File.Path)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
filter = func(m *gtsmodel.MediaAttachment) string {
|
|
||||||
return path.Join(mediaPath, m.File.Path)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attachments, err := list.GetAllAttachmentPaths(ctx, filter)
|
// Only write if any
|
||||||
if err != nil {
|
// string was prepared.
|
||||||
return err
|
if outbuf.Len() > 0 {
|
||||||
|
_, _ = os.Stdout.Write(outbuf.B)
|
||||||
|
outbuf.Reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, a := range attachments {
|
|
||||||
_, _ = list.out.WriteString(a + "\n")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListEmojis lists local, remote, or all emoji filepaths.
|
func printEmojiPaths(basePath string, emoji *gtsmodel.Emoji) {
|
||||||
var ListEmojis action.GTSAction = func(ctx context.Context) error {
|
// Append image path if present.
|
||||||
list, err := setupList(ctx)
|
if emoji.ImagePath != "" {
|
||||||
if err != nil {
|
path := pb.Join(basePath, emoji.ImagePath)
|
||||||
return err
|
_, _ = outbuf.WriteString(path + "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
// Append static path if present.
|
||||||
// Ensure lister gets shutdown on exit.
|
if emoji.ImageStaticPath != "" {
|
||||||
if err := list.shutdown(); err != nil {
|
path := pb.Join(basePath, emoji.ImageStaticPath)
|
||||||
log.Error(ctx, err)
|
_, _ = outbuf.WriteString(path + "\n")
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
var (
|
|
||||||
mediaPath = config.GetStorageLocalBasePath()
|
|
||||||
filter func(*gtsmodel.Emoji) string
|
|
||||||
)
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case list.localOnly:
|
|
||||||
filter = func(e *gtsmodel.Emoji) string {
|
|
||||||
if e.ImageRemoteURL != "" {
|
|
||||||
// Remote, not
|
|
||||||
// interested.
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return path.Join(mediaPath, e.ImagePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
case list.remoteOnly:
|
|
||||||
filter = func(e *gtsmodel.Emoji) string {
|
|
||||||
if e.ImageRemoteURL == "" {
|
|
||||||
// Local, not
|
|
||||||
// interested.
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return path.Join(mediaPath, e.ImagePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
filter = func(e *gtsmodel.Emoji) string {
|
|
||||||
return path.Join(mediaPath, e.ImagePath)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
emojis, err := list.GetAllEmojisPaths(ctx, filter)
|
// Only write if any
|
||||||
if err != nil {
|
// string was prepared.
|
||||||
return err
|
if outbuf.Len() > 0 {
|
||||||
|
_, _ = os.Stdout.Write(outbuf.B)
|
||||||
|
outbuf.Reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, e := range emojis {
|
|
||||||
_, _ = list.out.WriteString(e + "\n")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,17 @@ package prune
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
|
"code.superseriousbusiness.org/gotosocial/internal/gtscontext"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// check function conformance.
|
||||||
|
var _ action.GTSAction = All
|
||||||
|
|
||||||
// All performs all media clean actions
|
// All performs all media clean actions
|
||||||
var All action.GTSAction = func(ctx context.Context) error {
|
func All(ctx context.Context) error {
|
||||||
// Setup pruning utilities.
|
// Setup pruning utilities.
|
||||||
prune, err := setupPrune(ctx)
|
prune, err := setupPrune(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -21,13 +21,13 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/cleaner"
|
"code.superseriousbusiness.org/gotosocial/internal/cleaner"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"code.superseriousbusiness.org/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
|
"code.superseriousbusiness.org/gotosocial/internal/db/bundb"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
"code.superseriousbusiness.org/gotosocial/internal/media"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
"code.superseriousbusiness.org/gotosocial/internal/state"
|
||||||
gtsstorage "github.com/superseriousbusiness/gotosocial/internal/storage"
|
gtsstorage "code.superseriousbusiness.org/gotosocial/internal/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
type prune struct {
|
type prune struct {
|
||||||
|
|
@ -42,7 +42,9 @@ func setupPrune(ctx context.Context) (*prune, error) {
|
||||||
var state state.State
|
var state state.State
|
||||||
|
|
||||||
state.Caches.Init()
|
state.Caches.Init()
|
||||||
state.Caches.Start()
|
if err := state.Caches.Start(); err != nil {
|
||||||
|
return nil, fmt.Errorf("error starting caches: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Scheduler is required for the
|
// Scheduler is required for the
|
||||||
// cleaner, but no other workers
|
// cleaner, but no other workers
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,17 @@ package prune
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
|
"code.superseriousbusiness.org/gotosocial/internal/gtscontext"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// check function conformance.
|
||||||
|
var _ action.GTSAction = Orphaned
|
||||||
|
|
||||||
// Orphaned prunes orphaned media from storage.
|
// Orphaned prunes orphaned media from storage.
|
||||||
var Orphaned action.GTSAction = func(ctx context.Context) error {
|
func Orphaned(ctx context.Context) error {
|
||||||
// Setup pruning utilities.
|
// Setup pruning utilities.
|
||||||
prune, err := setupPrune(ctx)
|
prune, err := setupPrune(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -21,14 +21,17 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
|
"code.superseriousbusiness.org/gotosocial/internal/gtscontext"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// check function conformance.
|
||||||
|
var _ action.GTSAction = Remote
|
||||||
|
|
||||||
// Remote prunes old and/or unused remote media.
|
// Remote prunes old and/or unused remote media.
|
||||||
var Remote action.GTSAction = func(ctx context.Context) error {
|
func Remote(ctx context.Context) error {
|
||||||
// Setup pruning utilities.
|
// Setup pruning utilities.
|
||||||
prune, err := setupPrune(ctx)
|
prune, err := setupPrune(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -22,15 +22,18 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
|
"code.superseriousbusiness.org/gotosocial/internal/db/bundb"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
"code.superseriousbusiness.org/gotosocial/internal/state"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/trans"
|
"code.superseriousbusiness.org/gotosocial/internal/trans"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// check function conformance.
|
||||||
|
var _ action.GTSAction = Export
|
||||||
|
|
||||||
// Export exports info from the database into a file
|
// Export exports info from the database into a file
|
||||||
var Export action.GTSAction = func(ctx context.Context) error {
|
func Export(ctx context.Context) error {
|
||||||
var state state.State
|
var state state.State
|
||||||
|
|
||||||
// Only set state DB connection.
|
// Only set state DB connection.
|
||||||
|
|
|
||||||
|
|
@ -22,15 +22,18 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
|
"code.superseriousbusiness.org/gotosocial/internal/db/bundb"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
"code.superseriousbusiness.org/gotosocial/internal/state"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/trans"
|
"code.superseriousbusiness.org/gotosocial/internal/trans"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// check function conformance.
|
||||||
|
var _ action.GTSAction = Import
|
||||||
|
|
||||||
// Import imports info from a file into the database
|
// Import imports info from a file into the database
|
||||||
var Import action.GTSAction = func(ctx context.Context) error {
|
func Import(ctx context.Context) error {
|
||||||
var state state.State
|
var state state.State
|
||||||
|
|
||||||
// Only set state DB connection.
|
// Only set state DB connection.
|
||||||
|
|
|
||||||
|
|
@ -22,21 +22,21 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// check function conformance.
|
||||||
|
var _ action.GTSAction = Config
|
||||||
|
|
||||||
// Config just prints the collated config out to stdout as json.
|
// Config just prints the collated config out to stdout as json.
|
||||||
var Config action.GTSAction = func(ctx context.Context) (err error) {
|
func Config(ctx context.Context) (err error) {
|
||||||
var raw map[string]interface{}
|
var raw map[string]interface{}
|
||||||
|
|
||||||
// Marshal configuration to a raw JSON map
|
// Marshal configuration to a raw JSON map
|
||||||
config.Config(func(cfg *config.Configuration) {
|
config.Config(func(cfg *config.Configuration) {
|
||||||
raw, err = cfg.MarshalMap()
|
raw = cfg.MarshalMap()
|
||||||
})
|
})
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
enc := json.NewEncoder(os.Stdout)
|
enc := json.NewEncoder(os.Stdout)
|
||||||
enc.SetIndent("", " ")
|
enc.SetIndent("", " ")
|
||||||
|
|
|
||||||
68
cmd/gotosocial/action/migration/run.go
Normal file
68
cmd/gotosocial/action/migration/run.go
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
// 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 migration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/db/bundb"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
// check function conformance.
|
||||||
|
var _ action.GTSAction = Run
|
||||||
|
|
||||||
|
// Run will initialize the database, running any available migrations.
|
||||||
|
func Run(ctx context.Context) error {
|
||||||
|
var state state.State
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if state.DB != nil {
|
||||||
|
// Lastly, if database service was started,
|
||||||
|
// ensure it gets closed now all else stopped.
|
||||||
|
if err := state.DB.Close(); err != nil {
|
||||||
|
log.Errorf(ctx, "error stopping database: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally reached end of shutdown.
|
||||||
|
log.Info(ctx, "done! exiting...")
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Initialize caches
|
||||||
|
state.Caches.Init()
|
||||||
|
if err := state.Caches.Start(); err != nil {
|
||||||
|
return fmt.Errorf("error starting caches: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info(ctx, "starting db service...")
|
||||||
|
|
||||||
|
// Open connection to the database now caches started.
|
||||||
|
dbService, err := bundb.NewBunDBService(ctx, &state)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error creating dbservice: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set DB on state.
|
||||||
|
state.DB = dbService
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
@ -29,48 +30,53 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/admin"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/api"
|
||||||
|
apiutil "code.superseriousbusiness.org/gotosocial/internal/api/util"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/cleaner"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/db/bundb"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/email"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/federation"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/federation/federatingdb"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/filter/interaction"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/filter/mutes"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/filter/spam"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/filter/status"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/filter/visibility"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/httpclient"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/media"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/media/ffmpeg"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/messages"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/middleware"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/oauth"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/oauth/handlers"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/observability"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/oidc"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/processing"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/router"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/state"
|
||||||
|
gtsstorage "code.superseriousbusiness.org/gotosocial/internal/storage"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/subscriptions"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/transport"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/typeutils"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/web"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/webpush"
|
||||||
"github.com/KimMachineGun/automemlimit/memlimit"
|
"github.com/KimMachineGun/automemlimit/memlimit"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/admin"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
|
||||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/cleaner"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/federation/federatingdb"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/filter/interaction"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/filter/spam"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/httpclient"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/media/ffmpeg"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/middleware"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/observability"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oidc"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
|
||||||
tlprocessor "github.com/superseriousbusiness/gotosocial/internal/processing/timeline"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
|
||||||
gtsstorage "github.com/superseriousbusiness/gotosocial/internal/storage"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/subscriptions"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/timeline"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/transport"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/web"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/webpush"
|
|
||||||
"go.uber.org/automaxprocs/maxprocs"
|
"go.uber.org/automaxprocs/maxprocs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// check function conformance.
|
||||||
|
var _ action.GTSAction = Maintenance
|
||||||
|
var _ action.GTSAction = Start
|
||||||
|
|
||||||
// Maintenance starts and creates a GoToSocial server
|
// Maintenance starts and creates a GoToSocial server
|
||||||
// in maintenance mode (returns 503 for most requests).
|
// in maintenance mode (returns 503 for most requests).
|
||||||
var Maintenance action.GTSAction = func(ctx context.Context) error {
|
func Maintenance(ctx context.Context) error {
|
||||||
route, err := router.New(ctx)
|
route, err := router.New(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating maintenance router: %w", err)
|
return fmt.Errorf("error creating maintenance router: %w", err)
|
||||||
|
|
@ -99,7 +105,7 @@ var Maintenance action.GTSAction = func(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start creates and starts a gotosocial server
|
// Start creates and starts a gotosocial server
|
||||||
var Start action.GTSAction = func(ctx context.Context) error {
|
func Start(ctx context.Context) error {
|
||||||
// Set GOMAXPROCS / GOMEMLIMIT
|
// Set GOMAXPROCS / GOMEMLIMIT
|
||||||
// to match container limits.
|
// to match container limits.
|
||||||
setLimits(ctx)
|
setLimits(ctx)
|
||||||
|
|
@ -116,8 +122,9 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
)
|
)
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
// Stop caches with
|
// Stop any started caches.
|
||||||
// background tasks.
|
//
|
||||||
|
// Noop if never started.
|
||||||
state.Caches.Stop()
|
state.Caches.Stop()
|
||||||
|
|
||||||
if route != nil {
|
if route != nil {
|
||||||
|
|
@ -132,22 +139,10 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
// Stop any currently running
|
// Stop any currently running
|
||||||
// worker processes / scheduled
|
// worker processes / scheduled
|
||||||
// tasks from being executed.
|
// tasks from being executed.
|
||||||
|
//
|
||||||
|
// Noop on unstarted workers.
|
||||||
state.Workers.Stop()
|
state.Workers.Stop()
|
||||||
|
|
||||||
if state.Timelines.Home != nil {
|
|
||||||
// Home timeline mgr was setup, ensure it gets stopped.
|
|
||||||
if err := state.Timelines.Home.Stop(); err != nil {
|
|
||||||
log.Errorf(ctx, "error stopping home timeline: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if state.Timelines.List != nil {
|
|
||||||
// List timeline mgr was setup, ensure it gets stopped.
|
|
||||||
if err := state.Timelines.List.Stop(); err != nil {
|
|
||||||
log.Errorf(ctx, "error stopping list timeline: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if process != nil {
|
if process != nil {
|
||||||
const timeout = time.Minute
|
const timeout = time.Minute
|
||||||
|
|
||||||
|
|
@ -195,13 +190,15 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize tracing (noop if not enabled).
|
// Initialize tracing (noop if not enabled).
|
||||||
if err := observability.InitializeTracing(); err != nil {
|
if err := observability.InitializeTracing(ctx); err != nil {
|
||||||
return fmt.Errorf("error initializing tracing: %w", err)
|
return fmt.Errorf("error initializing tracing: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize caches
|
// Initialize caches
|
||||||
state.Caches.Init()
|
state.Caches.Init()
|
||||||
state.Caches.Start()
|
if err := state.Caches.Start(); err != nil {
|
||||||
|
return fmt.Errorf("error starting caches: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Open connection to the database now caches started.
|
// Open connection to the database now caches started.
|
||||||
dbService, err := bundb.NewBunDBService(ctx, state)
|
dbService, err := bundb.NewBunDBService(ctx, state)
|
||||||
|
|
@ -239,10 +236,17 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
return fmt.Errorf("error opening storage backend: %w", err)
|
return fmt.Errorf("error opening storage backend: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse http client allow
|
||||||
|
// and block range exceptions.
|
||||||
|
ranges, err := parseClientRanges()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare wrapped httpclient with config.
|
// Prepare wrapped httpclient with config.
|
||||||
client := httpclient.New(httpclient.Config{
|
client := httpclient.New(httpclient.Config{
|
||||||
AllowRanges: config.MustParseIPPrefixes(config.GetHTTPClientAllowIPs()),
|
AllowRanges: ranges.allow,
|
||||||
BlockRanges: config.MustParseIPPrefixes(config.GetHTTPClientBlockIPs()),
|
BlockRanges: ranges.block,
|
||||||
Timeout: config.GetHTTPClientTimeout(),
|
Timeout: config.GetHTTPClientTimeout(),
|
||||||
TLSInsecureSkipVerify: config.GetHTTPClientTLSInsecureSkipVerify(),
|
TLSInsecureSkipVerify: config.GetHTTPClientTLSInsecureSkipVerify(),
|
||||||
})
|
})
|
||||||
|
|
@ -260,13 +264,22 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
|
|
||||||
// Build handlers used in later initializations.
|
// Build handlers used in later initializations.
|
||||||
mediaManager := media.NewManager(state)
|
mediaManager := media.NewManager(state)
|
||||||
oauthServer := oauth.New(ctx, dbService)
|
oauthServer := oauth.New(ctx, state,
|
||||||
|
handlers.GetValidateURIHandler(ctx),
|
||||||
|
handlers.GetClientScopeHandler(ctx, state),
|
||||||
|
handlers.GetAuthorizeScopeHandler(),
|
||||||
|
handlers.GetInternalErrorHandler(ctx),
|
||||||
|
handlers.GetResponseErrorHandler(ctx),
|
||||||
|
handlers.GetUserAuthorizationHandler(),
|
||||||
|
)
|
||||||
typeConverter := typeutils.NewConverter(state)
|
typeConverter := typeutils.NewConverter(state)
|
||||||
visFilter := visibility.NewFilter(state)
|
visFilter := visibility.NewFilter(state)
|
||||||
|
muteFilter := mutes.NewFilter(state)
|
||||||
intFilter := interaction.NewFilter(state)
|
intFilter := interaction.NewFilter(state)
|
||||||
|
statusFilter := status.NewFilter(state)
|
||||||
spamFilter := spam.NewFilter(state)
|
spamFilter := spam.NewFilter(state)
|
||||||
federatingDB := federatingdb.New(state, typeConverter, visFilter, intFilter, spamFilter)
|
federatingDB := federatingdb.New(state, typeConverter, visFilter, intFilter, spamFilter)
|
||||||
transportController := transport.NewController(state, federatingDB, &federation.Clock{}, client)
|
transportController := transport.NewController(state, federatingDB, client)
|
||||||
federator := federation.NewFederator(
|
federator := federation.NewFederator(
|
||||||
state,
|
state,
|
||||||
federatingDB,
|
federatingDB,
|
||||||
|
|
@ -302,26 +315,6 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
// Create a Web Push notification sender.
|
// Create a Web Push notification sender.
|
||||||
webPushSender := webpush.NewSender(client, state, typeConverter)
|
webPushSender := webpush.NewSender(client, state, typeConverter)
|
||||||
|
|
||||||
// Initialize both home / list timelines.
|
|
||||||
state.Timelines.Home = timeline.NewManager(
|
|
||||||
tlprocessor.HomeTimelineGrab(state),
|
|
||||||
tlprocessor.HomeTimelineFilter(state, visFilter),
|
|
||||||
tlprocessor.HomeTimelineStatusPrepare(state, typeConverter),
|
|
||||||
tlprocessor.SkipInsert(),
|
|
||||||
)
|
|
||||||
if err := state.Timelines.Home.Start(); err != nil {
|
|
||||||
return fmt.Errorf("error starting home timeline: %s", err)
|
|
||||||
}
|
|
||||||
state.Timelines.List = timeline.NewManager(
|
|
||||||
tlprocessor.ListTimelineGrab(state),
|
|
||||||
tlprocessor.ListTimelineFilter(state, visFilter),
|
|
||||||
tlprocessor.ListTimelineStatusPrepare(state, typeConverter),
|
|
||||||
tlprocessor.SkipInsert(),
|
|
||||||
)
|
|
||||||
if err := state.Timelines.List.Start(); err != nil {
|
|
||||||
return fmt.Errorf("error starting list timeline: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the job scheduler
|
// Start the job scheduler
|
||||||
// (this is required for cleaner).
|
// (this is required for cleaner).
|
||||||
state.Workers.StartScheduler()
|
state.Workers.StartScheduler()
|
||||||
|
|
@ -363,7 +356,9 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
emailSender,
|
emailSender,
|
||||||
webPushSender,
|
webPushSender,
|
||||||
visFilter,
|
visFilter,
|
||||||
|
muteFilter,
|
||||||
intFilter,
|
intFilter,
|
||||||
|
statusFilter,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Schedule background cleaning tasks.
|
// Schedule background cleaning tasks.
|
||||||
|
|
@ -391,8 +386,13 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
return fmt.Errorf("error scheduling poll expiries: %w", err)
|
return fmt.Errorf("error scheduling poll expiries: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// schedule publication tasks for all scheduled statuses.
|
||||||
|
if err := process.Status().ScheduledStatusesScheduleAll(ctx); err != nil {
|
||||||
|
return fmt.Errorf("error scheduling status publications: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize metrics.
|
// Initialize metrics.
|
||||||
if err := observability.InitializeMetrics(state.DB); err != nil {
|
if err := observability.InitializeMetrics(ctx, state); err != nil {
|
||||||
return fmt.Errorf("error initializing metrics: %w", err)
|
return fmt.Errorf("error initializing metrics: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -493,26 +493,29 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
return fmt.Errorf("error generating session name for session middleware: %w", err)
|
return fmt.Errorf("error generating session name for session middleware: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Configure our instance cookie policy.
|
||||||
|
cookiePolicy := apiutil.NewCookiePolicy()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
authModule = api.NewAuth(dbService, process, idp, routerSession, sessionName) // auth/oauth paths
|
authModule = api.NewAuth(state, process, idp, routerSession, sessionName, cookiePolicy) // auth/oauth paths
|
||||||
clientModule = api.NewClient(state, process) // api client endpoints
|
clientModule = api.NewClient(state, process) // api client endpoints
|
||||||
metricsModule = api.NewMetrics() // Metrics endpoints
|
healthModule = api.NewHealth(dbService.Ready) // Health check endpoints
|
||||||
healthModule = api.NewHealth(dbService.Ready) // Health check endpoints
|
fileserverModule = api.NewFileserver(process) // fileserver endpoints
|
||||||
fileserverModule = api.NewFileserver(process) // fileserver endpoints
|
robotsModule = api.NewRobots() // robots.txt endpoint
|
||||||
robotsModule = api.NewRobots() // robots.txt endpoint
|
wellKnownModule = api.NewWellKnown(process) // .well-known endpoints
|
||||||
wellKnownModule = api.NewWellKnown(process) // .well-known endpoints
|
nodeInfoModule = api.NewNodeInfo(process) // nodeinfo endpoint
|
||||||
nodeInfoModule = api.NewNodeInfo(process) // nodeinfo endpoint
|
activityPubModule = api.NewActivityPub(dbService, process) // ActivityPub endpoints
|
||||||
activityPubModule = api.NewActivityPub(dbService, process) // ActivityPub endpoints
|
webModule = web.New(dbService, process, cookiePolicy) // web pages + user profiles + settings panels etc
|
||||||
webModule = web.New(dbService, process) // web pages + user profiles + settings panels etc
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create per-route / per-grouping middlewares.
|
// Create per-route / per-grouping middlewares.
|
||||||
// rate limiting
|
// rate limiting
|
||||||
rlLimit := config.GetAdvancedRateLimitRequests()
|
rlLimit := config.GetAdvancedRateLimitRequests()
|
||||||
clLimit := middleware.RateLimit(rlLimit, config.GetAdvancedRateLimitExceptionsParsed()) // client api
|
exceptions := config.GetAdvancedRateLimitExceptions()
|
||||||
s2sLimit := middleware.RateLimit(rlLimit, config.GetAdvancedRateLimitExceptionsParsed()) // server-to-server (AP)
|
clLimit := middleware.RateLimit(rlLimit, exceptions) // client api
|
||||||
fsMainLimit := middleware.RateLimit(rlLimit, config.GetAdvancedRateLimitExceptionsParsed()) // fileserver / web templates
|
s2sLimit := middleware.RateLimit(rlLimit, exceptions) // server-to-server (AP)
|
||||||
fsEmojiLimit := middleware.RateLimit(rlLimit*2, config.GetAdvancedRateLimitExceptionsParsed()) // fileserver (emojis only, use high limit)
|
fsMainLimit := middleware.RateLimit(rlLimit, exceptions) // fileserver / web templates
|
||||||
|
fsEmojiLimit := middleware.RateLimit(rlLimit*2, exceptions) // fileserver (emojis only, use high limit)
|
||||||
|
|
||||||
// throttling
|
// throttling
|
||||||
cpuMultiplier := config.GetAdvancedThrottlingMultiplier()
|
cpuMultiplier := config.GetAdvancedThrottlingMultiplier()
|
||||||
|
|
@ -549,7 +552,6 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
// apply throttling *after* rate limiting
|
// apply throttling *after* rate limiting
|
||||||
authModule.Route(route, clLimit, clThrottle, robotsDisallowAll, gzip)
|
authModule.Route(route, clLimit, clThrottle, robotsDisallowAll, gzip)
|
||||||
clientModule.Route(route, clLimit, clThrottle, robotsDisallowAll, gzip)
|
clientModule.Route(route, clLimit, clThrottle, robotsDisallowAll, gzip)
|
||||||
metricsModule.Route(route, clLimit, clThrottle, robotsDisallowAIOnly)
|
|
||||||
healthModule.Route(route, clLimit, clThrottle, robotsDisallowAIOnly)
|
healthModule.Route(route, clLimit, clThrottle, robotsDisallowAIOnly)
|
||||||
fileserverModule.Route(route, fsMainLimit, fsThrottle, robotsDisallowAIOnly)
|
fileserverModule.Route(route, fsMainLimit, fsThrottle, robotsDisallowAIOnly)
|
||||||
fileserverModule.RouteEmojis(route, instanceAccount.ID, fsEmojiLimit, fsThrottle, robotsDisallowAIOnly)
|
fileserverModule.RouteEmojis(route, instanceAccount.ID, fsEmojiLimit, fsThrottle, robotsDisallowAIOnly)
|
||||||
|
|
@ -609,3 +611,44 @@ func compileWASM(ctx context.Context) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseClientRanges() (
|
||||||
|
*struct {
|
||||||
|
allow []netip.Prefix
|
||||||
|
block []netip.Prefix
|
||||||
|
},
|
||||||
|
error,
|
||||||
|
) {
|
||||||
|
parseF := func(ips []string, ranges []netip.Prefix, flag string) error {
|
||||||
|
for i, ip := range ips {
|
||||||
|
p, err := netip.ParsePrefix(ip)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error parsing %s value %s: %w", flag, ip, err)
|
||||||
|
}
|
||||||
|
ranges[i] = p
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
allowIPs := config.GetHTTPClientAllowIPs()
|
||||||
|
allowRanges := make([]netip.Prefix, len(allowIPs))
|
||||||
|
allowFlag := config.HTTPClientAllowIPsFlag
|
||||||
|
if err := parseF(allowIPs, allowRanges, allowFlag); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
blockIPs := config.GetHTTPClientBlockIPs()
|
||||||
|
blockRanges := make([]netip.Prefix, len(blockIPs))
|
||||||
|
blockFlag := config.HTTPClientBlockIPsFlag
|
||||||
|
if err := parseF(blockIPs, blockRanges, blockFlag); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &struct {
|
||||||
|
allow []netip.Prefix
|
||||||
|
block []netip.Prefix
|
||||||
|
}{
|
||||||
|
allow: allowRanges,
|
||||||
|
block: blockRanges,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,15 @@
|
||||||
|
|
||||||
package testrig
|
package testrig
|
||||||
|
|
||||||
import "github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action"
|
||||||
|
)
|
||||||
|
|
||||||
|
// check function conformance.
|
||||||
|
var _ action.GTSAction = Start
|
||||||
|
|
||||||
// Start creates and starts a gotosocial testrig server.
|
// Start creates and starts a gotosocial testrig server.
|
||||||
// This is only enabled in debug builds, else is nil.
|
// This is only enabled in debug builds, else is nil.
|
||||||
var Start action.GTSAction
|
func Start(context.Context) error { return nil }
|
||||||
|
|
|
||||||
|
|
@ -28,34 +28,34 @@ import (
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/admin"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/api"
|
||||||
|
apiutil "code.superseriousbusiness.org/gotosocial/internal/api/util"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/cleaner"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/language"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/middleware"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/observability"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/oidc"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/router"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/state"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/storage"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/subscriptions"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/typeutils"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/web"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/testrig"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/admin"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
|
||||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/cleaner"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/language"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/middleware"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/observability"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oidc"
|
|
||||||
tlprocessor "github.com/superseriousbusiness/gotosocial/internal/processing/timeline"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/subscriptions"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/timeline"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/web"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// check function conformance.
|
||||||
|
var _ action.GTSAction = Start
|
||||||
|
|
||||||
// Start creates and starts a gotosocial testrig server.
|
// Start creates and starts a gotosocial testrig server.
|
||||||
// This is only enabled in debug builds, else is nil.
|
// This is only enabled in debug builds, else is nil.
|
||||||
var Start action.GTSAction = func(ctx context.Context) error {
|
func Start(ctx context.Context) error {
|
||||||
testrig.InitTestConfig()
|
testrig.InitTestConfig()
|
||||||
testrig.InitTestLog()
|
testrig.InitTestLog()
|
||||||
|
|
||||||
|
|
@ -89,29 +89,20 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
// tasks from being executed.
|
// tasks from being executed.
|
||||||
testrig.StopWorkers(state)
|
testrig.StopWorkers(state)
|
||||||
|
|
||||||
if state.Timelines.Home != nil {
|
|
||||||
// Home timeline mgr was setup, ensure it gets stopped.
|
|
||||||
if err := state.Timelines.Home.Stop(); err != nil {
|
|
||||||
log.Errorf(ctx, "error stopping home timeline: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if state.Timelines.List != nil {
|
|
||||||
// List timeline mgr was setup, ensure it gets stopped.
|
|
||||||
if err := state.Timelines.List.Stop(); err != nil {
|
|
||||||
log.Errorf(ctx, "error stopping list timeline: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if state.Storage != nil {
|
if state.Storage != nil {
|
||||||
// If storage was created, ensure torn down.
|
// If storage was created, ensure torn down.
|
||||||
testrig.StandardStorageTeardown(state.Storage)
|
testrig.StandardStorageTeardown(state.Storage)
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.DB != nil {
|
if state.DB != nil {
|
||||||
|
// Clean up database by
|
||||||
|
// dropping tables if required.
|
||||||
|
if !config.GetTestrigSkipDBTeardown() {
|
||||||
|
testrig.StandardDBTeardown(state.DB)
|
||||||
|
}
|
||||||
|
|
||||||
// Lastly, if database service was started,
|
// Lastly, if database service was started,
|
||||||
// ensure it gets closed now all else stopped.
|
// ensure it gets closed now all else stopped.
|
||||||
testrig.StandardDBTeardown(state.DB)
|
|
||||||
if err := state.DB.Close(); err != nil {
|
if err := state.DB.Close(); err != nil {
|
||||||
log.Errorf(ctx, "error stopping database: %v", err)
|
log.Errorf(ctx, "error stopping database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -127,7 +118,7 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
config.SetInstanceLanguages(parsedLangs)
|
config.SetInstanceLanguages(parsedLangs)
|
||||||
|
|
||||||
if err := observability.InitializeTracing(); err != nil {
|
if err := observability.InitializeTracing(ctx); err != nil {
|
||||||
return fmt.Errorf("error initializing tracing: %w", err)
|
return fmt.Errorf("error initializing tracing: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -142,7 +133,10 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
// that twice, we can just start the initialized caches.
|
// that twice, we can just start the initialized caches.
|
||||||
state.Caches.Start()
|
state.Caches.Start()
|
||||||
|
|
||||||
testrig.StandardDBSetup(state.DB, nil)
|
// Populate database tables + data if required.
|
||||||
|
if !config.GetTestrigSkipDBSetup() {
|
||||||
|
testrig.StandardDBSetup(state.DB, nil)
|
||||||
|
}
|
||||||
|
|
||||||
// Get the instance account (we'll need this later).
|
// Get the instance account (we'll need this later).
|
||||||
instanceAccount, err := state.DB.GetInstanceAccount(ctx, "")
|
instanceAccount, err := state.DB.GetInstanceAccount(ctx, "")
|
||||||
|
|
@ -170,27 +164,6 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
emailSender := testrig.NewEmailSender("./web/template/", nil)
|
emailSender := testrig.NewEmailSender("./web/template/", nil)
|
||||||
webPushSender := testrig.NewWebPushMockSender()
|
webPushSender := testrig.NewWebPushMockSender()
|
||||||
typeConverter := typeutils.NewConverter(state)
|
typeConverter := typeutils.NewConverter(state)
|
||||||
filter := visibility.NewFilter(state)
|
|
||||||
|
|
||||||
// Initialize both home / list 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)
|
|
||||||
}
|
|
||||||
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, webPushSender, mediaManager)
|
processor := testrig.NewTestProcessor(state, federator, emailSender, webPushSender, mediaManager)
|
||||||
|
|
||||||
|
|
@ -199,7 +172,7 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
defer testrig.StopWorkers(state)
|
defer testrig.StopWorkers(state)
|
||||||
|
|
||||||
// Initialize metrics.
|
// Initialize metrics.
|
||||||
if err := observability.InitializeMetrics(state.DB); err != nil {
|
if err := observability.InitializeMetrics(ctx, state); err != nil {
|
||||||
return fmt.Errorf("error initializing metrics: %w", err)
|
return fmt.Errorf("error initializing metrics: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -282,23 +255,24 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
return fmt.Errorf("error generating session name for session middleware: %w", err)
|
return fmt.Errorf("error generating session name for session middleware: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Configure our instance cookie policy.
|
||||||
|
cookiePolicy := apiutil.NewCookiePolicy()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
authModule = api.NewAuth(state.DB, processor, idp, routerSession, sessionName) // auth/oauth paths
|
authModule = api.NewAuth(state, processor, idp, routerSession, sessionName, cookiePolicy) // auth/oauth paths
|
||||||
clientModule = api.NewClient(state, processor) // api client endpoints
|
clientModule = api.NewClient(state, processor) // api client endpoints
|
||||||
metricsModule = api.NewMetrics() // Metrics endpoints
|
healthModule = api.NewHealth(state.DB.Ready) // Health check endpoints
|
||||||
healthModule = api.NewHealth(state.DB.Ready) // Health check endpoints
|
fileserverModule = api.NewFileserver(processor) // fileserver endpoints
|
||||||
fileserverModule = api.NewFileserver(processor) // fileserver endpoints
|
robotsModule = api.NewRobots() // robots.txt endpoint
|
||||||
robotsModule = api.NewRobots() // robots.txt endpoint
|
wellKnownModule = api.NewWellKnown(processor) // .well-known endpoints
|
||||||
wellKnownModule = api.NewWellKnown(processor) // .well-known endpoints
|
nodeInfoModule = api.NewNodeInfo(processor) // nodeinfo endpoint
|
||||||
nodeInfoModule = api.NewNodeInfo(processor) // nodeinfo endpoint
|
activityPubModule = api.NewActivityPub(state.DB, processor) // ActivityPub endpoints
|
||||||
activityPubModule = api.NewActivityPub(state.DB, processor) // ActivityPub endpoints
|
webModule = web.New(state.DB, processor, cookiePolicy) // web pages + user profiles + settings panels etc
|
||||||
webModule = web.New(state.DB, processor) // web pages + user profiles + settings panels etc
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// these should be routed in order
|
// these should be routed in order
|
||||||
authModule.Route(route)
|
authModule.Route(route)
|
||||||
clientModule.Route(route)
|
clientModule.Route(route)
|
||||||
metricsModule.Route(route)
|
|
||||||
healthModule.Route(route)
|
healthModule.Route(route)
|
||||||
fileserverModule.Route(route)
|
fileserverModule.Route(route)
|
||||||
fileserverModule.RouteEmojis(route, instanceAccount.ID)
|
fileserverModule.RouteEmojis(route, instanceAccount.ID)
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,12 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action/admin/account"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action/admin/media"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action/admin/media/prune"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action/admin/trans"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||||
"github.com/spf13/cobra"
|
"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"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func adminCommands() *cobra.Command {
|
func adminCommands() *cobra.Command {
|
||||||
|
|
@ -146,6 +146,19 @@ func adminCommands() *cobra.Command {
|
||||||
config.AddAdminAccountPassword(adminAccountPasswordCmd)
|
config.AddAdminAccountPassword(adminAccountPasswordCmd)
|
||||||
adminAccountCmd.AddCommand(adminAccountPasswordCmd)
|
adminAccountCmd.AddCommand(adminAccountPasswordCmd)
|
||||||
|
|
||||||
|
adminAccountDisable2FACmd := &cobra.Command{
|
||||||
|
Use: "disable-2fa",
|
||||||
|
Short: "disable 2fa for the given local account",
|
||||||
|
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.Disable2FA)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
config.AddAdminAccount(adminAccountDisable2FACmd)
|
||||||
|
adminAccountCmd.AddCommand(adminAccountDisable2FACmd)
|
||||||
|
|
||||||
adminCmd.AddCommand(adminAccountCmd)
|
adminCmd.AddCommand(adminAccountCmd)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,10 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type preRunArgs struct {
|
type preRunArgs struct {
|
||||||
|
|
@ -43,16 +43,16 @@ type preRunArgs struct {
|
||||||
// env vars or cli flag.
|
// env vars or cli flag.
|
||||||
func preRun(a preRunArgs) error {
|
func preRun(a preRunArgs) error {
|
||||||
if err := config.BindFlags(a.cmd); err != nil {
|
if err := config.BindFlags(a.cmd); err != nil {
|
||||||
return fmt.Errorf("error binding flags: %s", err)
|
return fmt.Errorf("error binding flags: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := config.Reload(); err != nil {
|
if err := config.LoadConfigFile(); err != nil {
|
||||||
return fmt.Errorf("error reloading config: %s", err)
|
return fmt.Errorf("error loading config file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !a.skipValidation {
|
if !a.skipValidation {
|
||||||
if err := config.Validate(); err != nil {
|
if err := config.Validate(); err != nil {
|
||||||
return fmt.Errorf("invalid config: %s", err)
|
return fmt.Errorf("invalid config: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,11 +64,17 @@ func preRun(a preRunArgs) error {
|
||||||
// context, after initializing any last-minute things like loggers etc.
|
// context, after initializing any last-minute things like loggers etc.
|
||||||
func run(ctx context.Context, action action.GTSAction) error {
|
func run(ctx context.Context, action action.GTSAction) error {
|
||||||
log.SetTimeFormat(config.GetLogTimestampFormat())
|
log.SetTimeFormat(config.GetLogTimestampFormat())
|
||||||
// Set the global log level from configuration
|
|
||||||
|
// Set the global log level from configuration.
|
||||||
if err := log.ParseLevel(config.GetLogLevel()); err != nil {
|
if err := log.ParseLevel(config.GetLogLevel()); err != nil {
|
||||||
return fmt.Errorf("error parsing log level: %w", err)
|
return fmt.Errorf("error parsing log level: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set global log output format from configuration.
|
||||||
|
if err := log.ParseFormat(config.GetLogFormat()); err != nil {
|
||||||
|
return fmt.Errorf("error parsing log format: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
if config.GetSyslogEnabled() {
|
if config.GetSyslogEnabled() {
|
||||||
// Enable logging to syslog
|
// Enable logging to syslog
|
||||||
if err := log.EnableSyslog(
|
if err := log.EnableSyslog(
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
configaction "code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action/debug/config"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
configaction "github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action/debug/config"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func debugCommands() *cobra.Command {
|
func debugCommands() *cobra.Command {
|
||||||
|
|
@ -39,7 +38,6 @@ func debugCommands() *cobra.Command {
|
||||||
return run(cmd.Context(), configaction.Config)
|
return run(cmd.Context(), configaction.Config)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
config.AddServerFlags(debugConfigCmd)
|
|
||||||
debugCmd.AddCommand(debugConfigCmd)
|
debugCmd.AddCommand(debugConfigCmd)
|
||||||
return debugCmd
|
return debugCmd
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,9 @@ import (
|
||||||
godebug "runtime/debug"
|
godebug "runtime/debug"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
_ "code.superseriousbusiness.org/gotosocial/docs"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
_ "github.com/superseriousbusiness/gotosocial/docs"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Version is the version of GoToSocial being used.
|
// Version is the version of GoToSocial being used.
|
||||||
|
|
@ -41,27 +40,22 @@ func main() {
|
||||||
// override version in config store
|
// override version in config store
|
||||||
config.SetSoftwareVersion(version)
|
config.SetSoftwareVersion(version)
|
||||||
|
|
||||||
// instantiate the root command
|
rootCmd := new(cobra.Command)
|
||||||
rootCmd := &cobra.Command{
|
rootCmd.Use = "gotosocial"
|
||||||
Use: "gotosocial",
|
rootCmd.Short = "GoToSocial - a fediverse social media server"
|
||||||
Short: "GoToSocial - a fediverse social media server",
|
rootCmd.Long = "GoToSocial - a fediverse social media server\n\nFor help, see: https://docs.gotosocial.org.\n\nCode: https://codeberg.org/superseriousbusiness/gotosocial"
|
||||||
Long: "GoToSocial - a fediverse social media server\n\nFor help, see: https://docs.gotosocial.org.\n\nCode: https://github.com/superseriousbusiness/gotosocial",
|
rootCmd.Version = version
|
||||||
Version: version,
|
rootCmd.SilenceErrors = true
|
||||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
rootCmd.SilenceUsage = true
|
||||||
// before running any other cmd funcs, we must load config-path
|
|
||||||
return config.LoadEarlyFlags(cmd)
|
|
||||||
},
|
|
||||||
SilenceErrors: true,
|
|
||||||
SilenceUsage: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
// attach global flags to the root command so that they can be accessed from any subcommand
|
// Register global flags with root.
|
||||||
config.AddGlobalFlags(rootCmd)
|
config.RegisterGlobalFlags(rootCmd)
|
||||||
|
|
||||||
// add subcommands
|
// Add subcommands with their flags.
|
||||||
rootCmd.AddCommand(serverCommands())
|
rootCmd.AddCommand(serverCommands())
|
||||||
rootCmd.AddCommand(debugCommands())
|
rootCmd.AddCommand(debugCommands())
|
||||||
rootCmd.AddCommand(adminCommands())
|
rootCmd.AddCommand(adminCommands())
|
||||||
|
rootCmd.AddCommand(migrationCommands())
|
||||||
|
|
||||||
// Testrigcmd will only be set when debug is enabled.
|
// Testrigcmd will only be set when debug is enabled.
|
||||||
if testrigCmd := testrigCommands(); testrigCmd != nil {
|
if testrigCmd := testrigCommands(); testrigCmd != nil {
|
||||||
|
|
@ -70,7 +64,7 @@ func main() {
|
||||||
log.Fatal("gotosocial must be built and run with the DEBUG enviroment variable set to enable and access testrig")
|
log.Fatal("gotosocial must be built and run with the DEBUG enviroment variable set to enable and access testrig")
|
||||||
}
|
}
|
||||||
|
|
||||||
// run
|
// Run the prepared root command.
|
||||||
if err := rootCmd.Execute(); err != nil {
|
if err := rootCmd.Execute(); err != nil {
|
||||||
log.Fatalf("error executing command: %s", err)
|
log.Fatalf("error executing command: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
43
cmd/gotosocial/migrations.go
Normal file
43
cmd/gotosocial/migrations.go
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
// 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 main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action/migration"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// migrationCommands returns the 'migrations' subcommand
|
||||||
|
func migrationCommands() *cobra.Command {
|
||||||
|
migrationCmd := &cobra.Command{
|
||||||
|
Use: "migrations",
|
||||||
|
Short: "gotosocial migrations-related tasks",
|
||||||
|
}
|
||||||
|
migrationRunCmd := &cobra.Command{
|
||||||
|
Use: "run",
|
||||||
|
Short: "starts and stops the database, running any outstanding migrations",
|
||||||
|
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(), migration.Run)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
migrationCmd.AddCommand(migrationRunCmd)
|
||||||
|
return migrationCmd
|
||||||
|
}
|
||||||
|
|
@ -18,9 +18,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action/server"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action/server"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// serverCommands returns the 'server' subcommand
|
// serverCommands returns the 'server' subcommand
|
||||||
|
|
@ -39,7 +38,6 @@ func serverCommands() *cobra.Command {
|
||||||
return run(cmd.Context(), server.Start)
|
return run(cmd.Context(), server.Start)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
config.AddServerFlags(serverStartCmd)
|
|
||||||
serverCmd.AddCommand(serverStartCmd)
|
serverCmd.AddCommand(serverStartCmd)
|
||||||
|
|
||||||
serverMaintenanceCmd := &cobra.Command{
|
serverMaintenanceCmd := &cobra.Command{
|
||||||
|
|
@ -52,7 +50,6 @@ func serverCommands() *cobra.Command {
|
||||||
return run(cmd.Context(), server.Maintenance)
|
return run(cmd.Context(), server.Maintenance)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
config.AddServerFlags(serverMaintenanceCmd)
|
|
||||||
serverCmd.AddCommand(serverMaintenanceCmd)
|
serverCmd.AddCommand(serverMaintenanceCmd)
|
||||||
|
|
||||||
return serverCmd
|
return serverCmd
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,14 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action/testrig"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||||
|
"codeberg.org/gruf/go-debug"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action/testrig"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func testrigCommands() *cobra.Command {
|
func testrigCommands() *cobra.Command {
|
||||||
if testrig.Start != nil {
|
if debug.DEBUG {
|
||||||
testrigCmd := &cobra.Command{
|
testrigCmd := &cobra.Command{
|
||||||
Use: "testrig",
|
Use: "testrig",
|
||||||
Short: "gotosocial testrig-related tasks",
|
Short: "gotosocial testrig-related tasks",
|
||||||
|
|
@ -38,6 +40,7 @@ func testrigCommands() *cobra.Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
testrigCmd.AddCommand(testrigStartCmd)
|
testrigCmd.AddCommand(testrigStartCmd)
|
||||||
|
config.AddTestrig(testrigCmd)
|
||||||
return testrigCmd
|
return testrigCmd
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -24,15 +24,15 @@ import (
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/db/bundb"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/media"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/media/ffmpeg"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/state"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/storage"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/util"
|
||||||
"codeberg.org/gruf/go-storage/memory"
|
"codeberg.org/gruf/go-storage/memory"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/media/ffmpeg"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
||||||
|
|
@ -19,19 +19,23 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/db/bundb"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/gtsmodel"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/media"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/media/ffmpeg"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/state"
|
||||||
|
"code.superseriousbusiness.org/gotosocial/internal/storage"
|
||||||
"codeberg.org/gruf/go-storage/memory"
|
"codeberg.org/gruf/go-storage/memory"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/media/ffmpeg"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
@ -39,7 +43,7 @@ func main() {
|
||||||
ctx, cncl := signal.NotifyContext(ctx, syscall.SIGTERM, syscall.SIGINT)
|
ctx, cncl := signal.NotifyContext(ctx, syscall.SIGTERM, syscall.SIGINT)
|
||||||
defer cncl()
|
defer cncl()
|
||||||
|
|
||||||
log.SetLevel(log.INFO)
|
log.SetLevel(log.ERROR)
|
||||||
|
|
||||||
if len(os.Args) != 4 {
|
if len(os.Args) != 4 {
|
||||||
log.Panic(ctx, "Usage: go run ./cmd/process-media <input-file> <output-processed> <output-thumbnail>")
|
log.Panic(ctx, "Usage: go run ./cmd/process-media <input-file> <output-processed> <output-thumbnail>")
|
||||||
|
|
@ -63,7 +67,8 @@ func main() {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
config.SetHost("example.com")
|
config.SetProtocol("http")
|
||||||
|
config.SetHost("localhost:8080")
|
||||||
config.SetStorageBackend("disk")
|
config.SetStorageBackend("disk")
|
||||||
config.SetStorageLocalBasePath("/tmp/gotosocial")
|
config.SetStorageLocalBasePath("/tmp/gotosocial")
|
||||||
config.SetDbType("sqlite")
|
config.SetDbType("sqlite")
|
||||||
|
|
@ -109,6 +114,7 @@ func main() {
|
||||||
log.Panic(ctx, err)
|
log.Panic(ctx, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
outputCopyable(media)
|
||||||
copyFile(ctx, &st, media.File.Path, os.Args[2])
|
copyFile(ctx, &st, media.File.Path, os.Args[2])
|
||||||
copyFile(ctx, &st, media.Thumbnail.Path, os.Args[3])
|
copyFile(ctx, &st, media.Thumbnail.Path, os.Args[3])
|
||||||
}
|
}
|
||||||
|
|
@ -136,3 +142,104 @@ func copyFile(ctx context.Context, st *storage.Driver, key string, path string)
|
||||||
log.Panic(ctx, err)
|
log.Panic(ctx, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func outputCopyable(media *gtsmodel.MediaAttachment) {
|
||||||
|
var (
|
||||||
|
now = time.Now()
|
||||||
|
nowStr = now.Format(time.RFC3339)
|
||||||
|
mediaType string
|
||||||
|
fileMetaExtra string
|
||||||
|
)
|
||||||
|
|
||||||
|
switch media.Type {
|
||||||
|
case gtsmodel.FileTypeImage:
|
||||||
|
mediaType = "gtsmodel.FileTypeImage"
|
||||||
|
case gtsmodel.FileTypeVideo:
|
||||||
|
mediaType = "gtsmodel.FileTypeVideo"
|
||||||
|
case gtsmodel.FileTypeGifv:
|
||||||
|
mediaType = "gtsmodel.FileTypeGifv"
|
||||||
|
case gtsmodel.FileTypeAudio:
|
||||||
|
mediaType = "gtsmodel.FileTypeAudio"
|
||||||
|
case gtsmodel.FileTypeUnknown:
|
||||||
|
mediaType = "gtsmodel.FileTypeUnknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
if media.FileMeta.Original.Duration != nil {
|
||||||
|
fileMetaExtra += fmt.Sprintf("\n\t\t\tDuration: util.Ptr[float32](%f),", *media.FileMeta.Original.Duration)
|
||||||
|
}
|
||||||
|
if media.FileMeta.Original.Framerate != nil {
|
||||||
|
fileMetaExtra += fmt.Sprintf("\n\t\t\tFramerate: util.Ptr[float32](%f),", *media.FileMeta.Original.Framerate)
|
||||||
|
}
|
||||||
|
if media.FileMeta.Original.Bitrate != nil {
|
||||||
|
fileMetaExtra += fmt.Sprintf("\n\t\t\tBitrate: util.Ptr[uint64](%d),", *media.FileMeta.Original.Bitrate)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf(`{
|
||||||
|
ID: "%s",
|
||||||
|
StatusID: "STATUS_ID_GOES_HERE",
|
||||||
|
URL: "%s",
|
||||||
|
RemoteURL: "",
|
||||||
|
CreatedAt: TimeMustParse("%s"),
|
||||||
|
Type: %s,
|
||||||
|
FileMeta: gtsmodel.FileMeta{
|
||||||
|
Original: gtsmodel.Original{
|
||||||
|
Width: %d,
|
||||||
|
Height: %d,
|
||||||
|
Size: %d,
|
||||||
|
Aspect: %f,%s
|
||||||
|
},
|
||||||
|
Small: gtsmodel.Small{
|
||||||
|
Width: %d,
|
||||||
|
Height: %d,
|
||||||
|
Size: %d,
|
||||||
|
Aspect: %f,
|
||||||
|
},
|
||||||
|
Focus: gtsmodel.Focus{
|
||||||
|
X: 0,
|
||||||
|
Y: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
AccountID: "ACCOUNT_ID_GOES_HERE",
|
||||||
|
Description: "DESCRIPTION_GOES_HERE",
|
||||||
|
ScheduledStatusID: "",
|
||||||
|
Blurhash: "%s",
|
||||||
|
Processing: 2,
|
||||||
|
File: gtsmodel.File{
|
||||||
|
Path: "%s",
|
||||||
|
ContentType: "%s",
|
||||||
|
FileSize: %d,
|
||||||
|
},
|
||||||
|
Thumbnail: gtsmodel.Thumbnail{
|
||||||
|
Path: "%s",
|
||||||
|
ContentType: "%s",
|
||||||
|
FileSize: %d,
|
||||||
|
URL: "%s",
|
||||||
|
RemoteURL: "",
|
||||||
|
},
|
||||||
|
Avatar: util.Ptr(false),
|
||||||
|
Header: util.Ptr(false),
|
||||||
|
Cached: util.Ptr(true),
|
||||||
|
}`+"\n",
|
||||||
|
media.ID,
|
||||||
|
strings.ReplaceAll(media.URL, media.AccountID, "ACCOUNT_ID_GOES_HERE"),
|
||||||
|
nowStr,
|
||||||
|
mediaType,
|
||||||
|
media.FileMeta.Original.Width,
|
||||||
|
media.FileMeta.Original.Height,
|
||||||
|
media.FileMeta.Original.Size,
|
||||||
|
media.FileMeta.Original.Aspect,
|
||||||
|
fileMetaExtra,
|
||||||
|
media.FileMeta.Small.Width,
|
||||||
|
media.FileMeta.Small.Height,
|
||||||
|
media.FileMeta.Small.Size,
|
||||||
|
media.FileMeta.Small.Aspect,
|
||||||
|
media.Blurhash,
|
||||||
|
strings.ReplaceAll(media.File.Path, media.AccountID, "ACCOUNT_ID_GOES_HERE"),
|
||||||
|
media.File.ContentType,
|
||||||
|
media.File.FileSize,
|
||||||
|
strings.ReplaceAll(media.Thumbnail.Path, media.AccountID, "ACCOUNT_ID_GOES_HERE"),
|
||||||
|
media.Thumbnail.ContentType,
|
||||||
|
media.Thumbnail.FileSize,
|
||||||
|
strings.ReplaceAll(media.Thumbnail.URL, media.AccountID, "ACCOUNT_ID_GOES_HERE"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -178,7 +178,7 @@ hooks:
|
||||||
|
|
||||||
For PostgreSQL, you'll want to use `postgresql_databases` instead.
|
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 media [`list-attachments`](cli.md#gotosocial-admin-media-list-attachments) and [`list-emojis`](cli.md#gotosocial-admin-media-list-emojis) commands. 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).
|
The file mentioned in `patterns_from` can be created by transforming the output from the GoToSocial CLI media [`list-attachments`](cli.md#gotosocial-admin-media-list-attachments) and [`list-emojis`](cli.md#gotosocial-admin-media-list-emojis) commands. In order to generate the right patterns you can use the [`media-to-borg-patterns.py`](https://codeberg.org/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.
|
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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ GoToSocial - a fediverse social media server
|
||||||
|
|
||||||
For help, see: https://docs.gotosocial.org.
|
For help, see: https://docs.gotosocial.org.
|
||||||
|
|
||||||
Code: https://github.com/superseriousbusiness/gotosocial
|
Code: https://codeberg.org/superseriousbusiness/gotosocial
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
gotosocial [command]
|
gotosocial [command]
|
||||||
|
|
@ -57,6 +57,9 @@ Contains `account`, `export`, `import`, and `media` subcommands.
|
||||||
|
|
||||||
This command can be used to create a new account on your instance.
|
This command can be used to create a new account on your instance.
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
You must have launched the server at least once before running this command, to initialize essential entries in the database.
|
||||||
|
|
||||||
`gotosocial admin account create --help`:
|
`gotosocial admin account create --help`:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
|
|
||||||
|
|
@ -41,9 +41,9 @@ With this in mind, you should only ever treat domain blocking as *one layer* of
|
||||||
|
|
||||||
Unfortunately, the Fediverse has its share of trolls, many of whom see domain blocking as an adversary to be defeated. To achieve this, they often target instances which use domain blocks to protect users.
|
Unfortunately, the Fediverse has its share of trolls, many of whom see domain blocking as an adversary to be defeated. To achieve this, they often target instances which use domain blocks to protect users.
|
||||||
|
|
||||||
As such, there are bots on the Fediverse which scrape instance domain blocks and announce any discovered blocks to the followers of the bot, opening the admin of the blocking instance up to harassment. These bots use the `api/v1/instance/peers?filter=suspended` endpoint of GoToSocial instances to gather domain block information.
|
As such, there are bots on the Fediverse which scrape instance domain blocks and announce any discovered blocks to the followers of the bot, opening the admin of the blocking instance up to harassment. These bots use the `api/v1/instance/peers?filter=suspended`, `api/v1/instance/peers?filter=blocked`, and/or `api/v1/instance/domain_blocks` endpoints of GoToSocial instances to gather domain block information.
|
||||||
|
|
||||||
By default, GoToSocial does not expose this endpoint publicly, so your instance will be safe from such scraping. However, if you set `instance-expose-suspended` to `true` in your config.yaml file, you may find that this endpoint gets scraped occasionally, and you may see your blocks being announced by troll bots.
|
By default, GoToSocial does not expose these endpoints publicly, so your instance will be safe from such scraping. However, if you set `instance-expose-blocklist` to `true` in your config.yaml file, you may find that these endpoints gets scraped occasionally, and you may see your blocks being announced by troll bots.
|
||||||
|
|
||||||
## What are the side effects of creating a domain block
|
## What are the side effects of creating a domain block
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,28 @@ The subscription with the higher priority is the one that now creates and manage
|
||||||
|
|
||||||
If the subscription with the higher priority is removed, then the next time all the subscriptions are fetched, "Less Important List" will create (or take ownership of) the domain allow instead.
|
If the subscription with the higher priority is removed, then the next time all the subscriptions are fetched, "Less Important List" will create (or take ownership of) the domain allow instead.
|
||||||
|
|
||||||
|
## Retractions
|
||||||
|
|
||||||
|
Sometimes, an entry that was present on a subscribed block or allow list will be removed later by the curator(s) of that list. When this happens, the removed domain permission entry can be said to have been "retracted".
|
||||||
|
|
||||||
|
For example, say your instance subscribes to one block list, and that block list contains an entry for `baddies.example.org`. A corresponding domain block for `baddies.example.org` has therefore been created in your database, with the subscription ID of your block list. In other words, the domain block is in force, and is managed by your block list subscription.
|
||||||
|
|
||||||
|
At some point, your instance fetches the list again, and this time it sees that the entry for `baddies.example.org` is no longer present in the list, because it has been removed by the list curator(s) (perhaps the admins turned their policies around, or the instance was shut down, etc). Thus, according to your instance, the block for `baddies.example.org` is now a "retracted" domain permission entry.
|
||||||
|
|
||||||
|
If the domain permission subscription is set to "Remove retracted permissions," then the now-retracted domain block will be removed from the database, and will no longer be enforced. In this example, that means your instance will start federating (again) with `baddies.example.org`.
|
||||||
|
|
||||||
|
If the domain permission subscription is *not* set to "Remove retracted permissions," then instead of the retracted block being removed from the database, it will be kept in the database but "orphaned" -- ie., it will still be in force, but it will be marked as no longer being managed by the subscription. In this example, that means your instance will keep blocking `baddies.example.org`.
|
||||||
|
|
||||||
|
!!!! Note "Retracted permissions and other subscriptions"
|
||||||
|
When a permission is retracted and removed from the database, but an entry for it exists on the list of *another* subscription of a lower priority than the one it was retracted from, then the permission will be recreated as an entry managed by the lower priority list.
|
||||||
|
|
||||||
|
For example, say you subscribe to List1 at priority 255, and List2 at priority 128, and `baddies.example.org` is present on both lists. That means the domain block entry will be managed by List1. If List1 later *retracts* the entry, it will be removed from your database (assuming you have "Remove retracted permissions" set). However, as soon as List2 is checked (usually seconds after List1), then an entry for `baddies.example.org` will be created again, but managed by List2 this time.
|
||||||
|
|
||||||
|
In other words, it is only when an entry is retracted from *every list you subscribe to* that it will truly be removed.
|
||||||
|
|
||||||
## Orphan Permissions
|
## Orphan Permissions
|
||||||
|
|
||||||
Domain permissions (blocks or allows) that are not currently managed by a domain permission subscription are considered "orphan" permissions. This includes permissions that an admin created in the settings panel by hand, or which were imported manually via the import/export page.
|
Domain permissions (blocks or allows) that are not currently managed by a domain permission subscription are considered "orphan" permissions. This includes permissions that an admin created in the settings panel by hand, entries which were imported manually via the import/export page, or entries that belonged to a subscription but have since been retracted but not removed.
|
||||||
|
|
||||||
If you wish, when creating a domain permission subscription, you can set ["adopt orphans"](./settings.md#adopt-orphan-permissions) to true for that subscription. If a domain permission subscription that is set to adopt orphans encounters an orphan permission which is *also present on the list at the subscription's URI*, then it will "adopt" the orphan by setting the orphan's subscription ID to its own ID.
|
If you wish, when creating a domain permission subscription, you can set ["adopt orphans"](./settings.md#adopt-orphan-permissions) to true for that subscription. If a domain permission subscription that is set to adopt orphans encounters an orphan permission which is *also present on the list at the subscription's URI*, then it will "adopt" the orphan by setting the orphan's subscription ID to its own ID.
|
||||||
|
|
||||||
|
|
@ -37,7 +56,7 @@ Domain permission subscriptions make it possible to easily create allowlist-fede
|
||||||
|
|
||||||
For example, instances `instance-a.example.org`, `instance-b.example.org`, and `instance-c.example.org` decide that they only want to federate with each other.
|
For example, instances `instance-a.example.org`, `instance-b.example.org`, and `instance-c.example.org` decide that they only want to federate with each other.
|
||||||
|
|
||||||
Using some version management platform like GitHub, they host a plaintext-formatted allowlist at something like `https://raw.githubusercontent.com/our-cluster/allowlist/refs/heads/main/allows.txt`.
|
Using some version management platform like Codeberg, they host a plaintext-formatted allowlist at something like `https://codeberg.org/our-cluster/allowlist/raw/branch/main/allows.txt`.
|
||||||
|
|
||||||
The contents of the plaintext-formatted allowlist are as follows:
|
The contents of the plaintext-formatted allowlist are as follows:
|
||||||
|
|
||||||
|
|
@ -47,7 +66,7 @@ instance-b.example.org
|
||||||
instance-c.example.org
|
instance-c.example.org
|
||||||
```
|
```
|
||||||
|
|
||||||
Each instance admin sets their federation mode to `allowlist`, and creates a subscription to create allows from `https://raw.githubusercontent.com/our-cluster/allowlist/refs/heads/main/allows.txt`, which results in domain allow entries being created for their own domain, and for each other domain in the cluster.
|
Each instance admin sets their federation mode to `allowlist`, and creates a subscription to create allows from `https://codeberg.org/our-cluster/allowlist/raw/branch/main/allows.txt`, which results in domain allow entries being created for their own domain, and for each other domain in the cluster.
|
||||||
|
|
||||||
At some point, someone from `instance-d.example.org` asks (out of band) whether they can be added to the cluster. The existing admins agree, and update their plaintext-formatted allowlist to read:
|
At some point, someone from `instance-d.example.org` asks (out of band) whether they can be added to the cluster. The existing admins agree, and update their plaintext-formatted allowlist to read:
|
||||||
|
|
||||||
|
|
@ -66,7 +85,7 @@ Domain permission subscriptions make it easy to collaborate on and subscribe to
|
||||||
|
|
||||||
For example, the admins of instances `instance-e.example.org`, `instance-f.example.org`, and `instance-g.example.org` decide that they are tired of duplicating work by playing whack-a-mole with bad actors. To make their lives easier, they decide to collaborate on a shared blocklist.
|
For example, the admins of instances `instance-e.example.org`, `instance-f.example.org`, and `instance-g.example.org` decide that they are tired of duplicating work by playing whack-a-mole with bad actors. To make their lives easier, they decide to collaborate on a shared blocklist.
|
||||||
|
|
||||||
Using some version management platform like GitHub, they host a blocklist at something like `https://raw.githubusercontent.com/baddies/blocklist/refs/heads/main/blocks.csv`.
|
Using some version management platform like Codeberg, they host a blocklist at something like `https://codeberg.org/our-cluster/allowlist/raw/branch/main/blocks.csv`.
|
||||||
|
|
||||||
When someone discovers a new domain hosting an instance they don't like, they can open a pull request or similar against the list, to add the questionable instance to the domain.
|
When someone discovers a new domain hosting an instance they don't like, they can open a pull request or similar against the list, to add the questionable instance to the domain.
|
||||||
|
|
||||||
|
|
@ -113,6 +132,27 @@ nothanks.com,suspend,false,false,,false
|
||||||
|
|
||||||
JSON lists use content type `application/json`.
|
JSON lists use content type `application/json`.
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"domain": "bumfaces.net",
|
||||||
|
"suspended_at": "2020-05-13T13:29:12.000Z",
|
||||||
|
"comment": "big jerks"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "peepee.poopoo",
|
||||||
|
"suspended_at": "2020-05-13T13:29:12.000Z",
|
||||||
|
"comment": "harassment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "nothanks.com",
|
||||||
|
"suspended_at": "2020-05-13T13:29:12.000Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
As an alternative to `"comment"`, `"public_comment"` will also work:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -31,13 +31,18 @@ When your instance encounters a mention or an announce of a status or account it
|
||||||
|
|
||||||
## Combining blocks and allows
|
## Combining blocks and allows
|
||||||
|
|
||||||
It is possible to both block and allow the same domain, and the effect of combining these two things depends on which federation mode your instance is currently using.
|
!!! danger
|
||||||
|
Combining blocks and allows is a tricky business!
|
||||||
|
|
||||||
|
When importing lists of allows and blocks, you should always review the list manually to make sure that you do not inadvertently block a domain that you would prefer not to block, since this can have **very annoying side effects** like removing follows/following, statuses, etc.
|
||||||
|
|
||||||
|
When in doubt, always add an explicit allow first as an insurance policy!
|
||||||
|
|
||||||

|
It is possible to both block and allow the same domain, and the effect of combining these two things depends on which federation mode your instance is currently using, as explained in the following subsections, which are summarised in a flowchart that you can find below.
|
||||||
|
|
||||||
### In blocklist mode
|
### In blocklist mode
|
||||||
|
|
||||||
As the chart shows, in blocklist mode (the left-hand part of the diagram), an explicit domain allow can be used to override a domain block.
|
As the chart below shows, in blocklist mode (the left-hand part of the diagram), an explicit domain allow can be used to override a domain block.
|
||||||
|
|
||||||
This is useful in cases where you are importing a blocklist from someone else, but the imported blocklist contains some instances you would actually prefer not to block. To avoid blocking those instances, you can create explicit domain allows for those instances first. Then, when you import the block list, the explicitly allowed domains will not be blocked, and the side effects of creating a block (deleting statuses, media, relationships etc) will not be processed.
|
This is useful in cases where you are importing a blocklist from someone else, but the imported blocklist contains some instances you would actually prefer not to block. To avoid blocking those instances, you can create explicit domain allows for those instances first. Then, when you import the block list, the explicitly allowed domains will not be blocked, and the side effects of creating a block (deleting statuses, media, relationships etc) will not be processed.
|
||||||
|
|
||||||
|
|
@ -47,16 +52,11 @@ Conversely, if you add an explicit allow for a domain that was blocked, the side
|
||||||
|
|
||||||
### In allowlist mode
|
### In allowlist mode
|
||||||
|
|
||||||
As the chart shows, in allowlist mode (the right-hand part of the diagram) an explicit domain block trumps an explicit domain allow. The following two things must be true in order for an instance to be allowed through, when running in allowlist mode:
|
As the chart below shows, in allowlist mode (the right-hand part of the diagram) an explicit domain block trumps an explicit domain allow. The following two things must be true in order for an instance to be allowed through, when running in allowlist mode:
|
||||||
|
|
||||||
1. An explicit domain block **does not exist** for the instance.
|
1. An explicit domain block **does not exist** for the instance.
|
||||||
2. An explicit domain allow **does exist** for the instance.
|
2. An explicit domain allow **does exist** for the instance.
|
||||||
|
|
||||||
If either of the above conditions are not met, the request will be denied.
|
If either of the above conditions are not met, the request will be denied.
|
||||||
|
|
||||||
!!! danger
|

|
||||||
Combining blocks and allows is a tricky business!
|
|
||||||
|
|
||||||
When importing lists of allows and blocks, you should always review the list manually to make sure that you do not inadvertently block a domain that you would prefer not to block, since this can have **very annoying side effects** like removing follows/following, statuses, etc.
|
|
||||||
|
|
||||||
When in doubt, always add an explicit allow first as an insurance policy!
|
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,6 @@ You can allow or disallow crawlers from collecting stats about your instance fro
|
||||||
|
|
||||||
The AI scrapers come from a [community maintained repository][airobots]. It's manually kept in sync for the time being. If you know of any missing robots, please send them a PR!
|
The AI scrapers come from a [community maintained repository][airobots]. It's manually kept in sync for the time being. If you know of any missing robots, please send them a PR!
|
||||||
|
|
||||||
A number of AI scrapers are known to ignore entries in `robots.txt` even if it explicitly matches their User-Agent. This means the `robots.txt` file is not a foolproof way of ensuring AI scrapers don't grab your content.
|
A number of AI scrapers are known to ignore entries in `robots.txt` even if it explicitly matches their User-Agent. This means the `robots.txt` file is not a foolproof way of ensuring AI scrapers don't grab your content. In addition to this you might want to look into blocking User-Agents via [requester header filtering](request_filtering_modes.md).
|
||||||
|
|
||||||
If you want to block these things fully, you'll need to block based on the User-Agent header in a reverse proxy until GoToSocial can filter requests by User-Agent header.
|
|
||||||
|
|
||||||
[airobots]: https://github.com/ai-robots-txt/ai.robots.txt/
|
[airobots]: https://github.com/ai-robots-txt/ai.robots.txt/
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,13 @@ If you check this box, then any existing domain permissions will become managed
|
||||||
1. They don't already have a subscription ID (ie., they're not managed by any domain permission subscription).
|
1. They don't already have a subscription ID (ie., they're not managed by any domain permission subscription).
|
||||||
2. They match a domain permission included in the list at the URL of this subscription.
|
2. They match a domain permission included in the list at the URL of this subscription.
|
||||||
|
|
||||||
For more information on orphan permissions, please see the separate [domain permission subscriptions](./domain_permission_subscriptions.md) document.
|
For more information on orphan permissions, please see the separate [domain permission subscriptions](./domain_permission_subscriptions.md#orphan-permissions) document.
|
||||||
|
|
||||||
|
##### Remove Retracted Permissions
|
||||||
|
|
||||||
|
This setting controls how retractions are handled by this domain permission subscription: if "Remove retracted permissions" is checked, retracted entries will be removed from the database; if "Remove retracted permissions" is not checked, retracted entries will just be orphaned instead.
|
||||||
|
|
||||||
|
For more detail on how retractions work, with examples, please see the separate [domain permission subscriptions](./domain_permission_subscriptions.md#retractions) document.
|
||||||
|
|
||||||
##### Create Permissions as Drafts
|
##### Create Permissions as Drafts
|
||||||
|
|
||||||
|
|
|
||||||
43
docs/admin/slow_hardware.md
Normal file
43
docs/admin/slow_hardware.md
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
# Managing GtS on slow hardware
|
||||||
|
|
||||||
|
While GoToSocial runs great on lower-end hardware, some operations are not practical on it, especially
|
||||||
|
instances with the database on slow storage (think anything that is not an SSD). This document
|
||||||
|
offers some suggestions on how to work around common issues when running GtS on slow hardware.
|
||||||
|
|
||||||
|
## Running database migrations on a different machine
|
||||||
|
|
||||||
|
Sometimes a database migration will need to do operations that are taxing on the database's storage.
|
||||||
|
These operations can take days if the database resides on a hard disk or SD card. If your
|
||||||
|
database is on slow storage, it can save a lot of time to follow the following procedure:
|
||||||
|
|
||||||
|
!!! danger
|
||||||
|
|
||||||
|
It might seem tempting to keep GtS running while you run the migrations on another machine, but
|
||||||
|
doing this will lead to all the posts that are received during the migration post disappearing
|
||||||
|
once the migrated database is re-imported.
|
||||||
|
|
||||||
|
1. Shut down GtS
|
||||||
|
2. Take a [backup](backup_and_restore.md#what-to-backup-database) of the database
|
||||||
|
3. Import the database on faster hardware
|
||||||
|
4. Run the GtS migration on the faster hardware
|
||||||
|
5. Take a backup of the resultant database
|
||||||
|
6. Import the resultant backup and overwrite the old database
|
||||||
|
7. Start GtS with the new version
|
||||||
|
|
||||||
|
### Running GtS migrations separately
|
||||||
|
|
||||||
|
After you import the database on the faster hardware, you can run the migration without starting
|
||||||
|
GtS by downloading the *target* GtS version from the [releases](https://codeberg.org/superseriousbusiness/gotosocial/releases) page.
|
||||||
|
For instance, if you are running `v0.19.2` and you want to upgrade to `v0.20.0-rc1`, download the
|
||||||
|
latter version. Once you have the binary, set it to executable by running `chmod u+x /path/to/gotosocial`. Afterwards, copy the configuration of the original server, and alter
|
||||||
|
it with the location of the new database. We copy the configuration in case variables like
|
||||||
|
the hostname is used in the migration, we want to keep that consistent.
|
||||||
|
Once everything is in place, you can run the migration like this:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ /path/to/gotosocial --config-path /path/to/config migrations run
|
||||||
|
```
|
||||||
|
|
||||||
|
This will run all the migrations, just like GtS would if it was started normally. Once this is done
|
||||||
|
you can copy the result to the original instance and start the new GtS version there as well, which
|
||||||
|
will see that everything is migrated and that there's nothing to do except run as expected.
|
||||||
|
|
@ -15,6 +15,10 @@ However! To enable folks to run **experimental, unsupported deployments of GoToS
|
||||||
|
|
||||||
A GoToSocial binary built with `nowasm` will use the [modernc version of SQLite](https://pkg.go.dev/modernc.org/sqlite) instead of the WASM one, and will use on-system `ffmpeg` and `ffprobe` binaries for media processing.
|
A GoToSocial binary built with `nowasm` will use the [modernc version of SQLite](https://pkg.go.dev/modernc.org/sqlite) instead of the WASM one, and will use on-system `ffmpeg` and `ffprobe` binaries for media processing.
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
To test if your system is compatible with the standard builds, you can use this command:
|
||||||
|
`if grep -qE '^flags.* (sse4|LSE)' /proc/cpuinfo; then echo "Your system is supporting GTS!"; else echo "Your system is not supporting GTS, you'll have to use the 'nowasm' builds :("; fi`
|
||||||
|
|
||||||
To build GoToSocial with the `nowasm` tag, you can pass the tag into our convenience `build.sh` script like so:
|
To build GoToSocial with the `nowasm` tag, you can pass the tag into our convenience `build.sh` script like so:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,89 @@
|
||||||
# Metrics
|
# Metrics
|
||||||
|
|
||||||
GoToSocial comes with [OpenTelemetry][otel] based metrics. The metrics are exposed using the [Prometheus exposition format][prom] on the `/metrics` path. The configuration settings are documented in the [Observability configuration reference][obs].
|
GoToSocial uses the [OpenTelemetry][otel] Go SDK to enable instance admins to expose runtime metrics in the Prometheus metrics format.
|
||||||
|
|
||||||
Currently the following metrics are collected:
|
Currently, the following metrics are collected:
|
||||||
|
|
||||||
* Go performance and runtime metrics
|
* Go performance and runtime metrics
|
||||||
* Gin (HTTP) metrics
|
* Gin (HTTP server) metrics
|
||||||
* Bun (database) metrics
|
* Bun (database) metrics
|
||||||
|
|
||||||
Metrics can be enable with the following configuration:
|
## Enabling metrics
|
||||||
|
|
||||||
|
To enable metrics, first set the `metrics-enabled` configuration value to `true` in your config.yaml file:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
metrics-enabled: true
|
metrics-enabled: true
|
||||||
```
|
```
|
||||||
|
|
||||||
Though metrics do not contain anything privacy sensitive, you may not want to allow just anyone to view and scrape operational metrics of your instance.
|
Then, you will need to set some additional environment variables on the GoToSocial process in order to configure OpenTelemetry to expose metrics in the Prometheus format:
|
||||||
|
|
||||||
|
```env
|
||||||
|
OTEL_METRICS_PRODUCERS=prometheus
|
||||||
|
OTEL_METRICS_EXPORTER=prometheus
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, this configuration will instantiate an additional HTTP server running alongside the standard GoToSocial HTTP server, which exposes a Prometheus metrics endpoint at `localhost:9464/metrics`.
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
If you are running GoToSocial using the [example systemd service definition](../../example/gotosocial.service), you can easily set these environment variables by uncommenting the relevant two lines in that file, and reloading + restarting the service.
|
||||||
|
|
||||||
|
If you wish, you can further customize this metrics HTTP server by using the following environment variables to change the host and port:
|
||||||
|
|
||||||
|
```env
|
||||||
|
OTEL_EXPORTER_PROMETHEUS_HOST=example.org
|
||||||
|
OTEL_EXPORTER_PROMETHEUS_PORT=9999
|
||||||
|
```
|
||||||
|
|
||||||
|
## Serving metrics to the outside world
|
||||||
|
|
||||||
|
If you have deployed GoToSocial in a "bare-metal" fashion without a reverse proxy, you can expose the metrics endpoint to the outside world by setting `OTEL_EXPORTER_PROMETHEUS_HOST` to your host value. For example, if your GtS instance `host` configuration value is set to `example.org`, you should set `OTEL_EXPORTER_PROMETHEUS_HOST=example.org`. You should then be able to access your metrics at `http://example.org:9464/metrics`. GoToSocial running in this fashion will not serve LetsEncrypt certificates at the metrics endpoint, so you will be limited to using HTTP rather than HTTPS.
|
||||||
|
|
||||||
|
If you are using a reverse proxy like Nginx, you can expose the metrics endpoint to the outside world with HTTPS certificates, by putting an additional location stanza in your Nginx configuration above the catch-all `location /` stanza:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
location /metrics {
|
||||||
|
proxy_pass http://127.0.0.1:9464;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This will instruct Nginx to forward requests to `example.org/metrics` to the separate Prometheus server running on port 9464.
|
||||||
|
|
||||||
## Enabling basic authentication
|
## Enabling basic authentication
|
||||||
|
|
||||||
You can enable basic authentication for the metrics endpoint. On the GoToSocial, side you'll need the following configuration:
|
Although there is no sensitive data contained in the OTEL runtime statistics exported by Prometheus, you may nevertheless wish to gate access to the `/metrics` endpoint behind some kind of authentication, to prevent every Tom, Dick, and Harry from looking at your runtime stats.
|
||||||
|
|
||||||
```yaml
|
You can do this by configuring your reverse proxy to require basic authentication for access to `/metrics`.
|
||||||
metrics-auth-enabled: true
|
|
||||||
metrics-auth-username: some_username
|
In Nginx, for example, you could do this by creating an `htpasswd` file alongside your site in the `sites-available` directory of Nginx, and instructing Nginx to use that file to gate access.
|
||||||
metrics-auth-password: some_password
|
|
||||||
|
Assuming you followed the [guide for setting up Nginx as your reverse proxy](../getting_started/reverse_proxy/nginx.md), you will already have a file for your Nginx service definition at `/etc/nginx/sites-available/example.org`, where `example.org` is the hostname of your instance.
|
||||||
|
|
||||||
|
You can create an `htpasswd` file alongside this file using the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
htpasswd -c /etc/nginx/sites-available/example.org.htpasswd username
|
||||||
```
|
```
|
||||||
|
|
||||||
You can scrape that endpoint with a Prometheus instance using the following configuration in your `scrape_configs`:
|
In the command, be sure to replace `example.org` with your hostname, and `username` with whatever username you want to use.
|
||||||
|
|
||||||
|
Now, edit `/etc/nginx/sites-available/example.org` and update your `/metrics` stanza to use the `httpasswd` file. After editing it should look something like this:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
location /metrics {
|
||||||
|
proxy_pass http://127.0.0.1:9464;
|
||||||
|
auth_basic "Metrics";
|
||||||
|
auth_basic_user_file /etc/nginx/sites-available/example.org.htpasswd;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Again, replace `example.org` in that snippet with your instance hostname.
|
||||||
|
|
||||||
|
When you're finished editing, reload + restart Nginx, and you should see a basic authentication prompt when visiting the `/metrics` endpoint of your instance in your browser.
|
||||||
|
|
||||||
|
## Prometheus scrape configuration
|
||||||
|
|
||||||
|
You can scrape your `/metrics` endpoint with a Prometheus instance using the following configuration in your `scrape_configs`:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- job_name: gotosocial
|
- job_name: gotosocial
|
||||||
|
|
@ -40,18 +97,12 @@ You can scrape that endpoint with a Prometheus instance using the following conf
|
||||||
- example.org
|
- example.org
|
||||||
```
|
```
|
||||||
|
|
||||||
## Blocking external scraping
|
Change `example.org` to your hostname in the above snippet. If you are not using HTTPS, change the `scheme` value to `http`. If you are not using basic authentication, you can remove the `basic_auth` section. If you are not using a reverse proxy, and metrics are exposed on port 9464, add the port to the host (eg., `example.org` -> `example.org:9464`).
|
||||||
|
|
||||||
When running with a reverse proxy you can use it to block external access to metrics. You can use this approach if your Prometheus scraper runs on the same machine as your GoToSocial instance and can thus access it internally.
|
## Viewing metrics on Grafana
|
||||||
|
|
||||||
For example with nginx, block the `/metrics` endpoint by returning a 404:
|
Instructions on how to set up Grafana are beyond the scope of this document. However, once you have set up a Grafana to pull from your Prometheus instance, you can import the [example Grafana dashboard](https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/example/metrics/gotosocial_grafana_dashboard.json) into your Grafana frontend to easily view GoToSocial Go runtime and HTTP metrics.
|
||||||
|
|
||||||
```nginx
|
|
||||||
location /metrics {
|
|
||||||
return 404;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
[otel]: https://opentelemetry.io/
|
[otel]: https://opentelemetry.io/
|
||||||
[prom]: https://prometheus.io/docs/instrumenting/exposition_formats/
|
[prom]: https://prometheus.io/docs/instrumenting/exposition_formats/
|
||||||
[obs]: ../configuration/observability.md
|
[obs]: ../configuration/observability_and_metrics.md
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ Different distributions have different sandboxing mechanisms they prefer and sup
|
||||||
We ship an example AppArmor policy for GoToSocial, which you can retrieve and install as follows:
|
We ship an example AppArmor policy for GoToSocial, which you can retrieve and install as follows:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ curl -LO 'https://github.com/superseriousbusiness/gotosocial/raw/main/example/apparmor/gotosocial'
|
$ curl -LO 'https://codeberg.org/superseriousbusiness/gotosocial/raw/main/example/apparmor/gotosocial'
|
||||||
$ sudo install -o root -g root gotosocial /etc/apparmor.d/gotosocial
|
$ sudo install -o root -g root gotosocial /etc/apparmor.d/gotosocial
|
||||||
$ sudo apparmor_parser -Kr /etc/apparmor.d/gotosocial
|
$ sudo apparmor_parser -Kr /etc/apparmor.d/gotosocial
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -32,4 +32,4 @@ You'll need to update the following settings:
|
||||||
* `db-sqlite-journal-mode`
|
* `db-sqlite-journal-mode`
|
||||||
* `db-sqlite-synchronous`
|
* `db-sqlite-synchronous`
|
||||||
|
|
||||||
We don't provide any recommendations as this will vary based on the solution you're using. See [this issue](https://github.com/superseriousbusiness/gotosocial/issues/3360#issuecomment-2380332027) for what you could potentially set those values to.
|
We don't provide any recommendations as this will vary based on the solution you're using. See [this issue](https://codeberg.org/superseriousbusiness/gotosocial/issues/3360#issuecomment-2380332027) for what you could potentially set those values to.
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,20 @@
|
||||||
# Tracing
|
# 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].
|
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 that may help you debug issues.
|
||||||
|
|
||||||
|
## Enabling tracing
|
||||||
|
|
||||||
|
To enable tracing on your instance, you must set `tracing-enabled` to `true` in your config.yaml file. Then, you must set the environment variable `OTEL_TRACES_EXPORTER` to your desired tracing format. A list of available options is available [here](https://opentelemetry.io/docs/languages/sdk-configuration/general/#otel_traces_exporter). Once you have changed your config and set the environment variable, restart your instance.
|
||||||
|
|
||||||
|
If necessary, you can do further configuration of tracing using the other environment variables listed [here](https://opentelemetry.io/docs/languages/sdk-configuration/general/).
|
||||||
|
|
||||||
|
## Ingesting traces
|
||||||
|
|
||||||
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.
|
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.
|
In [`example/tracing`][ext] we provide an example of how to do this using [Grafana Tempo][tempo] to ingest the spans and [Grafana][grafana] to explore them. You can use the files with `docker-compose up -d` to get Tempo and Grafana running.
|
||||||
|
|
||||||
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:
|
Please be aware that while the example configuration we provide can be used safely for local development and can provide a good starting point for setting up your own tracing infrastructure, it is not suitable for a so-called "production" setup.
|
||||||
|
|
||||||
```yaml
|
|
||||||
tracing-enabled: true
|
|
||||||
tracing-transport: "grpc"
|
|
||||||
tracing-endpoint: "localhost:4317"
|
|
||||||
tracing-insecure-transport: 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
|
## Querying and visualising traces
|
||||||
|
|
||||||
|
|
@ -27,18 +22,23 @@ Once you execute a few queries against your instance, you'll be able to find the
|
||||||
|
|
||||||
Using TraceQL, a simple query to find all traces related to requests to `/api/v1/instance` would look like this:
|
Using TraceQL, a simple query to find all traces related to requests to `/api/v1/instance` would look like this:
|
||||||
|
|
||||||
```
|
```traceql
|
||||||
{.http.route = "/api/v1/instance"}
|
{.http.route = "/api/v1/instance"}
|
||||||
```
|
```
|
||||||
|
|
||||||
If you wanted to see all GoToSocial traces, you could instead run:
|
If you wanted to see all GoToSocial traces, you could instead run:
|
||||||
|
|
||||||
```
|
```traceql
|
||||||
{.service.name = "GoToSocial"}
|
{.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.
|
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.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
[traceql]: https://grafana.com/docs/tempo/latest/traceql/
|
[traceql]: https://grafana.com/docs/tempo/latest/traceql/
|
||||||
|
[otel]: https://opentelemetry.io/
|
||||||
|
[obs]: ../configuration/observability_and_metrics.md
|
||||||
|
[tempo]: https://grafana.com/oss/tempo/
|
||||||
|
[grafana]: https://grafana.com/oss/grafana/
|
||||||
|
[ext]: https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/example/tracing
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
Using the client API requires authentication. This page documents the general flow for retrieving an authentication token with examples for doing this on the CLI using `curl`.
|
Using the client API requires authentication. This page documents the general flow for retrieving an authentication token with examples for doing this on the CLI using `curl`.
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
If you want to get an API access token via the settings panel instead, without having to use the command line, see the [Application documentation](https://docs.gotosocial.org/en/latest/user_guide/settings/#applications).
|
||||||
|
|
||||||
## Create a new application
|
## Create a new application
|
||||||
|
|
||||||
We need to register a new application, which we can then use to request an OAuth token. This is done by making a `POST` request to the `/api/v1/apps` endpoint. Replace `your_app_name` in the command below with the name you want to use for your application:
|
We need to register a new application, which we can then use to request an OAuth token. This is done by making a `POST` request to the `/api/v1/apps` endpoint. Replace `your_app_name` in the command below with the name you want to use for your application:
|
||||||
|
|
@ -19,18 +22,15 @@ curl \
|
||||||
|
|
||||||
The string `urn:ietf:wg:oauth:2.0:oob` is an indication of what is known as out-of-band authentication - a technique used in multi-factor authentication to reduce the number of ways that a bad actor can intrude on the authentication process. In this instance, it allows us to view and manually copy the tokens created to use further in this process.
|
The string `urn:ietf:wg:oauth:2.0:oob` is an indication of what is known as out-of-band authentication - a technique used in multi-factor authentication to reduce the number of ways that a bad actor can intrude on the authentication process. In this instance, it allows us to view and manually copy the tokens created to use further in this process.
|
||||||
|
|
||||||
Note that `scopes` can be any space-separated combination of:
|
!!! tip "Scopes"
|
||||||
|
It is always good practice to grant your application the lowest tier permissions it needs to do its job. e.g. If your application won't be making posts, use `scope=read` or even a subscope of that.
|
||||||
- `read`
|
|
||||||
- `write`
|
In this spirit, "read" is used in the example above, which means that the application will be restricted to only being able to do "read" actions.
|
||||||
- `admin`
|
|
||||||
|
For a list of available scopes, see [the swagger docs](https://docs.gotosocial.org/en/latest/api/swagger/).
|
||||||
|
|
||||||
!!! warning
|
!!! warning
|
||||||
GoToSocial does not currently support scoped authorization tokens, so any token you obtain in this process will be able to perform all actions on your behalf, including admin actions if your account has admin permissions. Nevertheless, it is always good practice to grant your application the lowest tier permissions it needs to do its job. e.g. If your application won't be making posts, use scope=read.
|
GoToSocial did not support scoped authorization tokens before version 0.19.0, so if you are using a version of GoToSocial below that, then any token you obtain in this process will be able to perform all actions on your behalf, including admin actions if your account has admin permissions.
|
||||||
|
|
||||||
In this spirit, "read" is used in the example above, which means that in the future when scoped tokens are supported, the application will be restricted to only being able to do "read" actions.
|
|
||||||
|
|
||||||
You can read more about additional planned OAuth security features [right here](https://github.com/superseriousbusiness/gotosocial/issues/2232).
|
|
||||||
|
|
||||||
A successful call returns a response with a `client_id` and `client_secret`, which we are going need to use in the rest of the process. It looks something like this:
|
A successful call returns a response with a `client_id` and `client_secret`, which we are going need to use in the rest of the process. It looks something like this:
|
||||||
|
|
||||||
|
|
@ -126,7 +126,6 @@ See this example:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl \
|
curl \
|
||||||
-H 'Content-Type: application/json' \
|
|
||||||
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
|
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
|
||||||
'https://example.org/api/v1/accounts/verify_credentials'
|
'https://example.org/api/v1/accounts/verify_credentials'
|
||||||
```
|
```
|
||||||
|
|
@ -140,7 +139,6 @@ For example, you can issue another `GET` request to the API using the same acces
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl \
|
curl \
|
||||||
-H 'Content-Type: application/json' \
|
|
||||||
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
|
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
|
||||||
'https://example.org/api/v1/notifications'
|
'https://example.org/api/v1/notifications'
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ Most of the GoToSocial API endpoints require a user-level OAuth token. For a gui
|
||||||
|
|
||||||
See the following issues for more context:
|
See the following issues for more context:
|
||||||
|
|
||||||
- [#1958](https://github.com/superseriousbusiness/gotosocial/issues/1958)
|
- [#1958](https://codeberg.org/superseriousbusiness/gotosocial/issues/1958)
|
||||||
- [#1944](https://github.com/superseriousbusiness/gotosocial/issues/1944)
|
- [#1944](https://codeberg.org/superseriousbusiness/gotosocial/issues/1944)
|
||||||
- [#2641](https://github.com/superseriousbusiness/gotosocial/issues/2641)
|
- [#2641](https://codeberg.org/superseriousbusiness/gotosocial/issues/2641)
|
||||||
|
|
||||||
<swagger-ui src="swagger.yaml"/>
|
<swagger-ui src="swagger.yaml"/>
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -64,4 +64,12 @@ accounts-allow-custom-css: false
|
||||||
# Examples: [500, 5000, 9999]
|
# Examples: [500, 5000, 9999]
|
||||||
# Default: 10000
|
# Default: 10000
|
||||||
accounts-custom-css-length: 10000
|
accounts-custom-css-length: 10000
|
||||||
|
|
||||||
|
# Int. The maximum number of profile fields allowed for each account.
|
||||||
|
#
|
||||||
|
# Note that going way higher than the default might break federation.
|
||||||
|
#
|
||||||
|
# Examples: [4, 6, 12]
|
||||||
|
# Default: 6
|
||||||
|
accounts-max-profile-fields: 6
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -2,21 +2,21 @@
|
||||||
|
|
||||||
GoToSocial stores statuses, accounts, etc, in a database. This can be either [SQLite](https://sqlite.org/index.html) or [Postgres](https://www.postgresql.org/).
|
GoToSocial stores statuses, accounts, etc, in a database. This can be either [SQLite](https://sqlite.org/index.html) or [Postgres](https://www.postgresql.org/).
|
||||||
|
|
||||||
By default, GoToSocial will use Postgres, but this is easy to change.
|
GoToSocial has no default configured database type or address. In most situations, we recommend the use of SQLite.
|
||||||
|
|
||||||
## SQLite
|
## SQLite
|
||||||
|
|
||||||
SQLite, as the name implies, is the lightest database type that GoToSocial can use. It stores entries in a simple file format, usually in the same directory as the GoToSocial binary itself. SQLite is great for small instances and single-board computers, where a dedicated database would be overkill.
|
SQLite, as the name implies, is the lightest database type that GoToSocial can use. It stores entries in a simple file format, usually in the same directory as the GoToSocial binary itself. SQLite is great for small instances and single-board computers, where a dedicated database would be overkill.
|
||||||
|
|
||||||
To configure GoToSocial to use SQLite, change `db-type` to `sqlite`. The `address` setting will then be a filename instead of an address, so you will want to change it to `sqlite.db` or something similar.
|
To use SQLite, keep the default `db-type` as `sqlite`. By default the database will be kept in the same directory as the GoToSocial binary, ie., `./sqlite.db`, but you can change this using `db-address` if you want to keep it somewhere else. In the same directory as the `sqlite.db` file, the SQLite driver will also create files `sqlite.db-wal` and `sqlite.db-shm` for temporary storage.
|
||||||
|
|
||||||
Note that the `:memory:` setting will use an *in-memory database* which will be wiped when your GoToSocial instance stops running. This is for testing only and is absolutely not suitable for running a proper instance, so *don't do this*.
|
Note that the `:memory:` setting will use an *in-memory database* which will be wiped when your GoToSocial instance stops running. This is for testing only and is absolutely not suitable for running a proper instance, so *don't do this*.
|
||||||
|
|
||||||
## Postgres
|
## Postgres
|
||||||
|
|
||||||
Postgres is a heavier database format, which is useful for larger instances where you need to scale performance, or where you need to run your database on a dedicated machine separate from your GoToSocial instance (or do funky stuff like run a database cluster).
|
Postgres (aka PostgreSQL) is a heavier database which is useful for larger instances where you need to run your database on a dedicated machine separate from your GoToSocial instance (or do funky stuff like run a database cluster).
|
||||||
|
|
||||||
You can connect to Postgres using either a Unix socket connection, or via TCP, depending on what you've set as your `db-address` value.
|
To configure GoToSocial to use Postgres, change `db-type` to `postgres`. The `db-address` setting will then be either a Unix socket connection or a hostname.
|
||||||
|
|
||||||
GoToSocial also supports connecting to Postgres using SSL/TLS over TCP. If you're running Postgres on a different machine from GoToSocial, and connecting to it via an IP address or hostname (as opposed to just running on localhost), then SSL/TLS is **CRUCIAL** to avoid leaking data all over the place!
|
GoToSocial also supports connecting to Postgres using SSL/TLS over TCP. If you're running Postgres on a different machine from GoToSocial, and connecting to it via an IP address or hostname (as opposed to just running on localhost), then SSL/TLS is **CRUCIAL** to avoid leaking data all over the place!
|
||||||
|
|
||||||
|
|
@ -42,6 +42,13 @@ create user gotosocial with password 'some_really_good_password';
|
||||||
grant all privileges on database gotosocial to gotosocial;
|
grant all privileges on database gotosocial to gotosocial;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you start using Postgres after 14, or you encounter `error executing command: error creating dbservice: db migration error: ERROR: permission denied for schema public`, you should grant `CREATE` permission to your db user *(This **must** be run in a postgres shell that's connected to the gotosocial database)*:
|
||||||
|
|
||||||
|
```psql
|
||||||
|
GRANT CREATE ON SCHEMA public TO gotosocial;
|
||||||
|
SELECT has_schema_privilege('gotosocial', 'public', 'CREATE'); -- should return t
|
||||||
|
```
|
||||||
|
|
||||||
GoToSocial makes use of ULIDs (Universally Unique Lexicographically Sortable Identifiers) which will not work in non-English collate environments. For this reason it is important to create the database with `C.UTF-8` locale. To do that on systems which were already initialized with non-C locale, `template0` pristine database template must be used.
|
GoToSocial makes use of ULIDs (Universally Unique Lexicographically Sortable Identifiers) which will not work in non-English collate environments. For this reason it is important to create the database with `C.UTF-8` locale. To do that on systems which were already initialized with non-C locale, `template0` pristine database template must be used.
|
||||||
|
|
||||||
If you want to use specific options when connecting to Postgres, you can use `db-postgres-connection-string` to define the connection string. If `db-postgres-connection-string` is defined, all other database related configuration fields will be ignored. For example, we can use `db-postgres-connection-string` to connect to `mySchema`, where the user name is `myUser` and password is `myPass` at `localhost` with the database name of `db`:
|
If you want to use specific options when connecting to Postgres, you can use `db-postgres-connection-string` to define the connection string. If `db-postgres-connection-string` is defined, all other database related configuration fields will be ignored. For example, we can use `db-postgres-connection-string` to connect to `mySchema`, where the user name is `myUser` and password is `myPass` at `localhost` with the database name of `db`:
|
||||||
|
|
@ -64,8 +71,8 @@ db-postgres-connection-string: 'postgres://myUser:myPass@localhost/db?search_pat
|
||||||
|
|
||||||
# String. Database type.
|
# String. Database type.
|
||||||
# Options: ["postgres","sqlite"]
|
# Options: ["postgres","sqlite"]
|
||||||
# Default: "postgres"
|
# Default: ""
|
||||||
db-type: "postgres"
|
db-type: "sqlite"
|
||||||
|
|
||||||
# String. Database address or parameters.
|
# String. Database address or parameters.
|
||||||
#
|
#
|
||||||
|
|
@ -80,29 +87,29 @@ db-type: "postgres"
|
||||||
#
|
#
|
||||||
# Examples: ["localhost","my.db.host","127.0.0.1","192.111.39.110",":memory:", "sqlite.db"]
|
# Examples: ["localhost","my.db.host","127.0.0.1","192.111.39.110",":memory:", "sqlite.db"]
|
||||||
# Default: ""
|
# Default: ""
|
||||||
db-address: ""
|
db-address: "sqlite.db"
|
||||||
|
|
||||||
# Int. Port for database connection.
|
# Int. Port for postgres database connection; ignored for sqlite.
|
||||||
# Examples: [5432, 1234, 6969]
|
# Examples: [5432, 1234, 6969]
|
||||||
# Default: 5432
|
# Default: 5432
|
||||||
db-port: 5432
|
db-port: 5432
|
||||||
|
|
||||||
# String. Username for the database connection.
|
# String. Username for postgres database connection.
|
||||||
# Examples: ["mydbuser","postgres","gotosocial"]
|
# Examples: ["mydbuser","postgres","gotosocial"]
|
||||||
# Default: ""
|
# Default: ""
|
||||||
db-user: ""
|
db-user: ""
|
||||||
|
|
||||||
# String. Password to use for the database connection
|
# String. Password to use for postgres database connection.
|
||||||
# Examples: ["password123","verysafepassword","postgres"]
|
# Examples: ["password123","verysafepassword","postgres"]
|
||||||
# Default: ""
|
# Default: ""
|
||||||
db-password: ""
|
db-password: ""
|
||||||
|
|
||||||
# String. Name of the database to use within the provided database type.
|
# String. Name of the database to use for postgres database connection.
|
||||||
# Examples: ["mydb","postgres","gotosocial"]
|
# Examples: ["mydb","postgres","gotosocial"]
|
||||||
# Default: "gotosocial"
|
# Default: "gotosocial"
|
||||||
db-database: "gotosocial"
|
db-database: "gotosocial"
|
||||||
|
|
||||||
# String. Disable, enable, or require SSL/TLS connection to the database.
|
# String. Disable, enable, or require SSL/TLS connection to postgres.
|
||||||
# If "disable" then no TLS connection will be attempted.
|
# If "disable" then no TLS connection will be attempted.
|
||||||
# If "enable" then TLS will be tried, but the database certificate won't be checked (for self-signed certs).
|
# If "enable" then TLS will be tried, but the database certificate won't be checked (for self-signed certs).
|
||||||
# If "require" then TLS will be required to make a connection, and a valid certificate must be presented.
|
# If "require" then TLS will be required to make a connection, and a valid certificate must be presented.
|
||||||
|
|
@ -139,7 +146,7 @@ db-tls-ca-cert: ""
|
||||||
#
|
#
|
||||||
# PLEASE NOTE!!: This setting currently only applies for Postgres. SQLite will always use 1 connection regardless
|
# 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.
|
# 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.
|
# See https://codeberg.org/superseriousbusiness/gotosocial/issues/1407 for more details.
|
||||||
#
|
#
|
||||||
# Examples: [16, 8, 10, 2]
|
# Examples: [16, 8, 10, 2]
|
||||||
# Default: 8
|
# Default: 8
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,19 @@ log-db-queries: false
|
||||||
# Default: true
|
# Default: true
|
||||||
log-client-ip: true
|
log-client-ip: true
|
||||||
|
|
||||||
|
# String. Format to use for formatting log entries.
|
||||||
|
# Supports "logfmt" and "json", with examples below:
|
||||||
|
#
|
||||||
|
# logfmt:
|
||||||
|
# func=router.(*Router).Start.func1 level=INFO msg="listening on 127.0.0.1:8080"
|
||||||
|
#
|
||||||
|
# json:
|
||||||
|
# {"func":"router.(*Router).Start.func1", "level":"INFO", "msg":"listening on 127.0.0.1:8080"}
|
||||||
|
#
|
||||||
|
# Examples: ["logfmt", "json"]
|
||||||
|
# Default: "logfmt"
|
||||||
|
log-format: "logfmt"
|
||||||
|
|
||||||
# String. Format to use for the timestamp in log lines.
|
# String. Format to use for the timestamp in log lines.
|
||||||
# If set to the empty string, the timestamp will be
|
# If set to the empty string, the timestamp will be
|
||||||
# ommitted from the logs entirely.
|
# ommitted from the logs entirely.
|
||||||
|
|
@ -94,14 +107,16 @@ account-domain: ""
|
||||||
# Default: "https"
|
# Default: "https"
|
||||||
protocol: "https"
|
protocol: "https"
|
||||||
|
|
||||||
# String. Address to bind the GoToSocial server to.
|
# String. Address to bind the GoToSocial HTTP server to.
|
||||||
# This can be an IPv4 address or an IPv6 address (surrounded in square brackets), or a hostname.
|
# This can be an IPv4 address, an IPv6 address, or a hostname.
|
||||||
|
#
|
||||||
# The default value will bind to all interfaces, which makes the server
|
# The default value will bind to all interfaces, which makes the server
|
||||||
# accessible by other machines. For most setups there is no need to change this.
|
# accessible by other machines. For most setups you won't need to change this.
|
||||||
# If you are using GoToSocial in a reverse proxy setup with the proxy running on
|
# However, if you are using GoToSocial in a reverse proxy setup with the proxy
|
||||||
# the same machine, you will want to set this to "localhost" or an equivalent,
|
# running on the same machine, you may want to set this to "localhost" or equivalent,
|
||||||
# so that the proxy can't be bypassed.
|
# so that the proxy definitely can't be bypassed.
|
||||||
# Examples: ["0.0.0.0", "172.128.0.16", "localhost", "[::]", "[2001:db8::fed1]"]
|
#
|
||||||
|
# Examples: ["0.0.0.0", "172.128.0.16", "localhost", "::1", "2001:db8::fed1"]
|
||||||
# Default: "0.0.0.0"
|
# Default: "0.0.0.0"
|
||||||
bind-address: "0.0.0.0"
|
bind-address: "0.0.0.0"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,9 +60,18 @@ http-client:
|
||||||
#
|
#
|
||||||
# THIS SETTING SHOULD BE USED FOR TESTING ONLY! IF YOU TURN THIS
|
# THIS SETTING SHOULD BE USED FOR TESTING ONLY! IF YOU TURN THIS
|
||||||
# ON WHILE RUNNING IN PRODUCTION YOU ARE LEAVING YOUR SERVER WIDE
|
# ON WHILE RUNNING IN PRODUCTION YOU ARE LEAVING YOUR SERVER WIDE
|
||||||
# OPEN TO MAN IN THE MIDDLE ATTACKS! DO NOT CHANGE THIS SETTING
|
# 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.
|
# UNLESS YOU KNOW EXACTLY WHAT YOU'RE DOING AND WHY YOU'RE DOING IT.
|
||||||
#
|
#
|
||||||
# Default: false
|
# Default: false
|
||||||
tls-insecure-skip-verify: false
|
tls-insecure-skip-verify: false
|
||||||
|
|
||||||
|
# Bool. Sets outgoing queries to webfinger, host-meta and nodeinfo to use
|
||||||
|
# HTTP instead of HTTPS.
|
||||||
|
#
|
||||||
|
# THIS SETTING SHOULD BE USED FOR TESTING ONLY! DO NOT CHANGE THIS SETTING
|
||||||
|
# UNLESS YOU KNOW EXACTLY WHAT YOU'RE DOING AND WHY YOU'RE DOING IT.
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
insecure-outgoing: false
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ gotosocial --config-path ./config.yaml server start
|
||||||
|
|
||||||
The command expects a file in [YAML](https://en.wikipedia.org/wiki/YAML) or [JSON](https://en.wikipedia.org/wiki/JSON) format.
|
The command expects a file in [YAML](https://en.wikipedia.org/wiki/YAML) or [JSON](https://en.wikipedia.org/wiki/JSON) format.
|
||||||
|
|
||||||
An example configuration file, with an explanation of each of the config fields, with default and example values, can be found [here](https://github.com/superseriousbusiness/gotosocial/blob/main/example/config.yaml). This example file is also included in release downloads.
|
An example configuration file, with an explanation of each of the config fields, with default and example values, can be found [here](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/example/config.yaml). This example file is also included in release downloads.
|
||||||
|
|
||||||
It's recommended to create your own configuration file with only the settings you need to change for your installation. This ensures you don't have to reconcile changes to defaults or adding/updating/removing settings from your configuration file that you haven't changed away from the defaults on every release.
|
It's recommended to create your own configuration file with only the settings you need to change for your installation. This ensures you don't have to reconcile changes to defaults or adding/updating/removing settings from your configuration file that you haven't changed away from the defaults on every release.
|
||||||
|
|
||||||
|
|
@ -125,7 +125,7 @@ This means in cases where you want to just try changing one thing, but don't wan
|
||||||
|
|
||||||
Reasonable default values are provided for *most* of the configuration parameters, except in cases where a custom value is absolutely required.
|
Reasonable default values are provided for *most* of the configuration parameters, except in cases where a custom value is absolutely required.
|
||||||
|
|
||||||
See the [example config file](https://github.com/superseriousbusiness/gotosocial/blob/main/example/config.yaml) for the default values, or run `gotosocial --help`.
|
See the [example config file](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/example/config.yaml) for the default values, or run `gotosocial --help`.
|
||||||
|
|
||||||
## `GTS_WAZERO_COMPILATION_CACHE`
|
## `GTS_WAZERO_COMPILATION_CACHE`
|
||||||
|
|
||||||
|
|
@ -135,7 +135,7 @@ To speed up startup time of GoToSocial, you can cache the compiled modules betwe
|
||||||
|
|
||||||
You can instruct GoToSocial on where to store the Wazero artifacts by setting the environment variable `GTS_WAZERO_COMPILATION_CACHE` to a directory, which will be used by GtS to store two smallish artifacts of ~50MiB or so each (~100MiB total).
|
You can instruct GoToSocial on where to store the Wazero artifacts by setting the environment variable `GTS_WAZERO_COMPILATION_CACHE` to a directory, which will be used by GtS to store two smallish artifacts of ~50MiB or so each (~100MiB total).
|
||||||
|
|
||||||
For an example of this in action, see the [docker-compose.yaml](https://raw.githubusercontent.com/superseriousbusiness/gotosocial/main/example/docker-compose/docker-compose.yaml), and the [gotosocial.service](https://raw.githubusercontent.com/superseriousbusiness/gotosocial/main/example/gotosocial.service) example files.
|
For an example of this in action, see the [docker-compose.yaml](https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/example/docker-compose/docker-compose.yaml), and the [gotosocial.service](https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/example/gotosocial.service) example files.
|
||||||
|
|
||||||
If you want to provide this value to GtS outside of systemd or Docker, you can do so in the following manner when starting up your GtS server:
|
If you want to provide this value to GtS outside of systemd or Docker, you can do so in the following manner when starting up your GtS server:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,29 +76,61 @@ instance-federation-mode: "blocklist"
|
||||||
# Default: false
|
# Default: false
|
||||||
instance-federation-spam-filter: false
|
instance-federation-spam-filter: false
|
||||||
|
|
||||||
# Bool. Allow unauthenticated users to make queries to /api/v1/instance/peers?filter=open in order
|
# Bool. Allow unauthenticated users to make queries to /api/v1/instance/peers?filter=open
|
||||||
# to see a list of instances that this instance 'peers' with. Even if set to 'false', then authenticated
|
# in order to see a list of domains that this instance 'peers' with.
|
||||||
# users (members of the instance) will still be able to query the endpoint.
|
#
|
||||||
|
# Even if set to 'false', then authenticated users (members of the instance)
|
||||||
|
# will still be able to query these endpoints using an OAuth token.
|
||||||
|
#
|
||||||
# Options: [true, false]
|
# Options: [true, false]
|
||||||
# Default: false
|
# Default: false
|
||||||
instance-expose-peers: false
|
instance-expose-peers: false
|
||||||
|
|
||||||
# Bool. Allow unauthenticated users to make queries to /api/v1/instance/peers?filter=suspended in order
|
# Bool. Allow unauthenticated users to make queries to the following instance API
|
||||||
# to see a list of instances that this instance blocks/suspends. Even if set to 'false', then authenticated
|
# endpoints in order to see a list of domains that this instance explicitly blocks,
|
||||||
# users (members of the instance) will still be able to query the endpoint.
|
# including the public reason for each block:
|
||||||
#
|
#
|
||||||
# WARNING: Setting this variable to 'true' may result in your instance being scraped by blocklist scrapers.
|
# - /api/v1/instance/peers?filter=blocklist
|
||||||
|
# - /api/v1/instance/peers?filter=suspended (deprecated)
|
||||||
|
# - /api/v1/instance/domain_blocks
|
||||||
|
#
|
||||||
|
# Even if set to 'false', then authenticated users (members of the instance)
|
||||||
|
# will still be able to query these endpoints using an OAuth token.
|
||||||
|
#
|
||||||
|
# WARNING: Setting to 'true' may result in your instance being targeted by blocklist scrapers.
|
||||||
# See: https://docs.gotosocial.org/en/latest/admin/domain_blocks/#block-announce-bots
|
# See: https://docs.gotosocial.org/en/latest/admin/domain_blocks/#block-announce-bots
|
||||||
#
|
#
|
||||||
# Options: [true, false]
|
# Options: [true, false]
|
||||||
# Default: false
|
# Default: false
|
||||||
instance-expose-suspended: false
|
instance-expose-blocklist: false
|
||||||
|
|
||||||
# Bool. Allow unauthenticated users to view /about/suspended,
|
# Bool. Allow unauthenticated users to view /about/domain_blocks,
|
||||||
# showing the HTML rendered list of instances that this instance blocks/suspends.
|
# which shows an HTML-rendered list of domains that this instance blocks,
|
||||||
|
# including the public reason for each block.
|
||||||
# Options: [true, false]
|
# Options: [true, false]
|
||||||
# Default: false
|
# Default: false
|
||||||
instance-expose-suspended-web: false
|
instance-expose-blocklist-web: false
|
||||||
|
|
||||||
|
# Bool. Allow unauthenticated users to make queries to the following instance API
|
||||||
|
# endpoints in order to see a list of domains that this instance explicitly allows
|
||||||
|
# including the public reason for each allow:
|
||||||
|
#
|
||||||
|
# - /api/v1/instance/peers?filter=allowlist
|
||||||
|
# - /api/v1/instance/domain_allows
|
||||||
|
#
|
||||||
|
# Even if set to 'false', then authenticated users (members of the instance)
|
||||||
|
# will still be able to query these endpoints using an OAuth token.
|
||||||
|
#
|
||||||
|
# Options: [true, false]
|
||||||
|
# Default: false
|
||||||
|
instance-expose-allowlist: false
|
||||||
|
|
||||||
|
# Bool. Allow unauthenticated users to view /about/domain_allows,
|
||||||
|
# which shows an HTML-rendered list of domains that this instance allows,
|
||||||
|
# including the public reason for each allow.
|
||||||
|
# Options: [true, false]
|
||||||
|
# Default: false
|
||||||
|
instance-expose-allowlist-web: false
|
||||||
|
|
||||||
# Bool. Allow unauthenticated users to make queries to /api/v1/timelines/public in order
|
# 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
|
# to see a list of public posts on this server. Even if set to 'false', then authenticated
|
||||||
|
|
@ -107,6 +139,21 @@ instance-expose-suspended-web: false
|
||||||
# Default: false
|
# Default: false
|
||||||
instance-expose-public-timeline: false
|
instance-expose-public-timeline: false
|
||||||
|
|
||||||
|
# Bool. Allow unauthenticated access to /api/v1/custom_emojis, which
|
||||||
|
# exposes the list of custom emojis available to users on this server.
|
||||||
|
#
|
||||||
|
# Setting this to 'true' may alleviate issues with clients that do not
|
||||||
|
# provide an access token in their requests to the emojis endpoint, but
|
||||||
|
# it also means that anyone can look at what emojis are installed on your
|
||||||
|
# instance, which may present privacy / safety issues.
|
||||||
|
#
|
||||||
|
# Even if set to 'false', authenticated users (ie., instance members)
|
||||||
|
# will still be able to query the endpoint using an access token.
|
||||||
|
#
|
||||||
|
# Options: [true, false]
|
||||||
|
# Default: false
|
||||||
|
instance-expose-custom-emojis: false
|
||||||
|
|
||||||
# Bool. This flag tweaks whether GoToSocial will deliver ActivityPub messages
|
# Bool. This flag tweaks whether GoToSocial will deliver ActivityPub messages
|
||||||
# to the shared inbox of a recipient, if one is available, instead of delivering
|
# to the shared inbox of a recipient, if one is available, instead of delivering
|
||||||
# each message to each actor who should receive a message individually.
|
# each message to each actor who should receive a message individually.
|
||||||
|
|
@ -174,9 +221,7 @@ instance-stats-mode: ""
|
||||||
|
|
||||||
# Bool. This flag controls whether local accounts may backdate statuses
|
# Bool. This flag controls whether local accounts may backdate statuses
|
||||||
# using past dates with the scheduled_at param to /api/v1/statuses.
|
# using past dates with the scheduled_at param to /api/v1/statuses.
|
||||||
# This flag does not affect scheduling posts in the future
|
# This flag can't prevent remote accounts from backdating their own statuses.
|
||||||
# (which is currently not implemented anyway),
|
|
||||||
# nor can it prevent remote accounts from backdating their own statuses.
|
|
||||||
#
|
#
|
||||||
# If true, all local accounts may backdate statuses.
|
# If true, all local accounts may backdate statuses.
|
||||||
# If false, status backdating will be disabled and an error will be returned if it's used.
|
# If false, status backdating will be disabled and an error will be returned if it's used.
|
||||||
|
|
|
||||||
|
|
@ -50,9 +50,9 @@ media-remote-max-size: 40MiB
|
||||||
media-description-min-chars: 0
|
media-description-min-chars: 0
|
||||||
|
|
||||||
# Int. Maximum amount of characters permitted in an image or video description.
|
# Int. Maximum amount of characters permitted in an image or video description.
|
||||||
# Examples: [1000, 1500, 3000]
|
# Examples: [1000, 5000, 10000]
|
||||||
# Default: 1500
|
# Default: 5000
|
||||||
media-description-max-chars: 1500
|
media-description-max-chars: 5000
|
||||||
|
|
||||||
# Size. Max size in bytes of emojis uploaded to this instance via the admin API.
|
# Size. Max size in bytes of emojis uploaded to this instance via the admin API.
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
# Observability
|
|
||||||
|
|
||||||
These settings let you tune and configure certain observability related behaviours.
|
|
||||||
|
|
||||||
## Metrics
|
|
||||||
|
|
||||||
Before enabling metrics, [read the guide](../advanced/metrics.md) and ensure you've taken the appropriate security measures for your setup.
|
|
||||||
|
|
||||||
## 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 "http" for OTLP HTTP.
|
|
||||||
# Options: ["grpc", "http"]
|
|
||||||
# Default: "grpc"
|
|
||||||
tracing-transport: "grpc"
|
|
||||||
|
|
||||||
# String. Endpoint of the trace ingester. When using the gRPC or HTTP based
|
|
||||||
# transports, provide the endpoint as a single address/port combination without a
|
|
||||||
# protocol scheme.
|
|
||||||
# Examples: ["localhost:4317"]
|
|
||||||
# Default: ""
|
|
||||||
tracing-endpoint: ""
|
|
||||||
|
|
||||||
# Bool. Disable TLS for the gRPC and HTTP transport protocols.
|
|
||||||
# Default: false
|
|
||||||
tracing-insecure-transport: false
|
|
||||||
|
|
||||||
# Bool. Enable OpenTelemetry based metrics support.
|
|
||||||
# Default: false
|
|
||||||
metrics-enabled: false
|
|
||||||
|
|
||||||
# Bool. Enable HTTP Basic Authentication for Prometheus metrics endpoint.
|
|
||||||
# Default: false
|
|
||||||
metrics-auth-enabled: false
|
|
||||||
|
|
||||||
# String. Username for Prometheus metrics endpoint.
|
|
||||||
# Default: ""
|
|
||||||
metrics-auth-username: ""
|
|
||||||
|
|
||||||
# String. Password for Prometheus metrics endpoint.
|
|
||||||
# Default: ""
|
|
||||||
metrics-auth-password: ""
|
|
||||||
```
|
|
||||||
63
docs/configuration/observability_and_metrics.md
Normal file
63
docs/configuration/observability_and_metrics.md
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
# Observability and Metrics
|
||||||
|
|
||||||
|
These settings let you tune and configure certain observability related behaviours.
|
||||||
|
|
||||||
|
GoToSocial uses OpenTelemetry. The metrics and trace exporters can be configured using the standard OpenTelemetry SDK environment variables. For a full reference, see [the OpenTelemetry docs](https://opentelemetry.io/docs/languages/sdk-configuration/).
|
||||||
|
|
||||||
|
## Metrics
|
||||||
|
|
||||||
|
Before enabling metrics, [read the guide](../advanced/metrics.md) and ensure you've taken the appropriate security measures for your setup.
|
||||||
|
|
||||||
|
If you want to expose metrics with (basic) authentication, you'll need to do this with a reverse proxy.
|
||||||
|
|
||||||
|
For more information and examples, see the [GtS metrics documentation](https://docs.gotosocial.org/en/latest/advanced/metrics/).
|
||||||
|
|
||||||
|
## Settings
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
##############################################
|
||||||
|
##### OBSERVABILITY AND METRICS 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.
|
||||||
|
#
|
||||||
|
# When enabling tracing, you must also configure a traces
|
||||||
|
# exporter using the OTEL environment variable documented here:
|
||||||
|
#
|
||||||
|
# https://opentelemetry.io/docs/languages/sdk-configuration/general/#otel_traces_exporter
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
tracing-enabled: false
|
||||||
|
|
||||||
|
# Bool. Enable OpenTelemetry based metrics support.
|
||||||
|
#
|
||||||
|
# To expose Prometheus metrics, you must configure a metrics producer and
|
||||||
|
# a metrics exporter, using the OTEL environment variables documented here:
|
||||||
|
#
|
||||||
|
# https://pkg.go.dev/go.opentelemetry.io/contrib/exporters/autoexport#NewMetricReader
|
||||||
|
#
|
||||||
|
# Typically, you will want to set the following environment variables
|
||||||
|
# (take note of the plural "producers" and singular "exporter"):
|
||||||
|
#
|
||||||
|
# - OTEL_METRICS_PRODUCERS=prometheus
|
||||||
|
# - OTEL_METRICS_EXPORTER=prometheus
|
||||||
|
#
|
||||||
|
# With these variables set, a Prometheus metrics endpoint will be exposed at
|
||||||
|
# localhost:9464/metrics. This can be further configured using the variables:
|
||||||
|
#
|
||||||
|
# - OTEL_EXPORTER_PROMETHEUS_HOST
|
||||||
|
# - OTEL_EXPORTER_PROMETHEUS_PORT
|
||||||
|
#
|
||||||
|
# For more information, see the GtS metrics documentation here:
|
||||||
|
#
|
||||||
|
# https://docs.gotosocial.org/en/latest/advanced/metrics/
|
||||||
|
#
|
||||||
|
# Default: false
|
||||||
|
metrics-enabled: false
|
||||||
|
```
|
||||||
|
|
@ -20,6 +20,9 @@ To validate your configuration, you can use the "Administration -> Actions -> Em
|
||||||
!!! info
|
!!! info
|
||||||
For safety reasons, the smtp library used by GoToSocial will refuse to send authentication credentials over an unencrypted connection, unless the mail provider is running on localhost.
|
For safety reasons, the smtp library used by GoToSocial will refuse to send authentication credentials over an unencrypted connection, unless the mail provider is running on localhost.
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
If your SMTP server offers `STARTTLS` in its EHLO response GoToSocial will try to use it. The SMTP server must hence also have valid SSL certificates. If you're sending mail via localhost and don't want to set up certificates make sure that your SMTP server doesn't announce STARTTLS support. In postfix this can be done via `-o smtpd_tls_security_level=none`.
|
||||||
|
|
||||||
## Settings
|
## Settings
|
||||||
|
|
||||||
The configuration options for smtp are as follows:
|
The configuration options for smtp are as follows:
|
||||||
|
|
|
||||||
|
|
@ -35,4 +35,14 @@ statuses-poll-option-max-chars: 50
|
||||||
# Examples: [4, 6, 10]
|
# Examples: [4, 6, 10]
|
||||||
# Default: 6
|
# Default: 6
|
||||||
statuses-media-max-files: 6
|
statuses-media-max-files: 6
|
||||||
|
|
||||||
|
# Int. Maximum number of statuses a user can schedule at time.
|
||||||
|
# Examples: [300]
|
||||||
|
# Default: 300
|
||||||
|
scheduled-statuses-max-total: 300
|
||||||
|
|
||||||
|
# Int. Maximum number of statuses a user can schedule for a single day.
|
||||||
|
# Examples: [25]
|
||||||
|
# Default: 25
|
||||||
|
scheduled-statuses-max-daily: 25
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,33 @@ storage-s3-secret-key: ""
|
||||||
# Examples: ["gts","cool-instance"]
|
# Examples: ["gts","cool-instance"]
|
||||||
# Default: ""
|
# Default: ""
|
||||||
storage-s3-bucket: ""
|
storage-s3-bucket: ""
|
||||||
|
|
||||||
|
|
||||||
|
# String. Key prefix to use for the S3 storage.
|
||||||
|
# This is optional.
|
||||||
|
#
|
||||||
|
# Prefix must end with a trailing slash.
|
||||||
|
#
|
||||||
|
# This is useful if you want to store multiple instances in the same bucket,
|
||||||
|
# or if you want to store your data in a subdirectory of the bucket.
|
||||||
|
# This has no effect if the storage backend isn't "s3".
|
||||||
|
#
|
||||||
|
# Examples: ["gts-instance1/", "gts-instance2/"]
|
||||||
|
# Default: ""
|
||||||
|
storage-s3-key-prefix: ""
|
||||||
|
|
||||||
|
# String. Bucket lookup type.
|
||||||
|
#
|
||||||
|
# If you know what kind of bucket lookup type you need you can specify it here.
|
||||||
|
#
|
||||||
|
# Many providers are no longer offering path-style bucket access and only subdomain-style. Then,
|
||||||
|
# you will need to specify "dns" here instead of the default "auto".
|
||||||
|
#
|
||||||
|
# If your provider only supports path-style bucket access, specify "path".
|
||||||
|
#
|
||||||
|
# Examples: ["path", "dns", "auto"]
|
||||||
|
# Default: "auto"
|
||||||
|
storage-s3-bucket-lookup: "auto"
|
||||||
```
|
```
|
||||||
|
|
||||||
## AWS S3 Configuration
|
## AWS S3 Configuration
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,14 @@
|
||||||
|
|
||||||
To correctly enforce [rate limiting](../api/ratelimiting.md), GoToSocial relies on the concept of "trusted proxies" in order to accurately determine the IP address of clients accessing your server.
|
To correctly enforce [rate limiting](../api/ratelimiting.md), GoToSocial relies on the concept of "trusted proxies" in order to accurately determine the IP address of clients accessing your server.
|
||||||
|
|
||||||
A "trusted proxy" is an intermediate network hop that GoToSocial can be instructed to trust to provide a correct client IP address.
|
A "trusted proxy" is an intermediate network hop that GoToSocial can be instructed to trust to provide a correct client IP address via an `X-Forwarded-For` or `X-Real-IP` header.
|
||||||
|
|
||||||
For example, if you are running in a reverse proxy configuration with Docker + Nginx, then the Docker network address of Nginx should be configured as a trusted proxy, since all traffic from the wider internet will come into GoToSocial via Nginx.
|
For example, if you are running in a reverse proxy configuration with Docker + Nginx, then the Docker network address of Nginx should be configured as a trusted proxy, since all traffic from the wider internet will come into GoToSocial via Nginx.
|
||||||
|
|
||||||
Without setting `trusted-proxies` correctly, GoToSocial will see all incoming client IP addresses as the same address, which leads to rate limiting issues, since GoToSocial uses client IP addresses to bucket rate limits.
|
If `trusted-proxies` is not set correctly, GoToSocial will see all incoming client IP addresses as the same address. This will lead to rate limiting issues, since GoToSocial uses client IP addresses to bucket rate limits.
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
If you reached this page via the trusted proxies warning on a GoToSocial page, and the suggested address to add is `127.0.0.1`, then before you try any of the other troubleshooting steps, ensure that your reverse proxy is configured to correctly pass either `X-Forwarded-For` or `X-Real-IP`.
|
||||||
|
|
||||||
## tl;dr: How to set `trusted-proxies` correctly
|
## tl;dr: How to set `trusted-proxies` correctly
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,4 +38,4 @@ We introduced a sign-up flow in v0.16.0. The server you want to sign up to must
|
||||||
|
|
||||||
## Why's it still in Beta?
|
## Why's it still in Beta?
|
||||||
|
|
||||||
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.
|
Take a look at the [list of open bugs](https://codeberg.org/superseriousbusiness/gotosocial/issues?labels=378161) and the [roadmap](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/ROADMAP.md) for a more detailed rundown.
|
||||||
|
|
|
||||||
|
|
@ -248,6 +248,90 @@ Another difference between GoToSocial and other server implementations is that G
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
|
## `hidesToPublicFromUnauthedWeb` and `hidesCcPublicFromUnauthedWeb`
|
||||||
|
|
||||||
|
GoToSocial uses the properties `hidesToPublicFromUnauthedWeb` and `hidesCcPublicFromUnauthedWeb` to indicate whether an actor prefers to hide posts addressed `to` or `cc` public from unauthenticated (ie., logged-out) visitors to web pages, web apps, and web APIs.
|
||||||
|
|
||||||
|
Some background for this: many ActivityPub server softwares allow unauthenticated visitors to the profile web page of an actor to see a list of posts that an actor has created that are addressed either `to` or `cc` public. These are often called "public" posts, and "unlisted", "unlocked", or "quiet public" posts, respectively. GoToSocial provides [a settings flag](../user_guide/settings.md#visibility-level-of-posts-to-show-on-your-profile) that allows GtS accounts to hide posts from the web view of their profile, as one layer of protection to make it more of a nuisance to scrape/stalk someone with a GtS account.
|
||||||
|
|
||||||
|
While this setting works for hiding posts of an actor *on their own instance*, prior to GoToSocial v0.20.0, this preference was not federated out to other instances, nor was it federated in from other instances. This leads to two problems:
|
||||||
|
|
||||||
|
1. Many other fedi server softwares permit logged-out visitors, via a web app, to look up profiles of *remote* accounts, and to see public and unlisted posts created by those accounts. This means that it is trivial to work around the ability of GtS users to hide their posts from the web. For example, say a GtS user at `@someone@gts.example.org` locks down their profile by setting the visibility of posts on their profile to "none"; this prevents visitors to `gts.example.org` from seeing posts, but one could visit eg. `mastodon.example.org` and, while logged out, look up `@someone@gts.example.org`, and see all the posts there that have been sent to, or dereferenced by, actors on `mastodon.example.org`. This makes the GtS user's choice to hide their posts significantly less meaningful.
|
||||||
|
2. In an effort to support this extra layer of privacy, by default GoToSocial instances do not show posts from remote instances unless they are addressed `to` public. For example, if someone from `mastodon.example.org` were to reply to a post by `@someone@gts.example.org`, and the reply was only addressed `cc` public instead of `to` public, the GtS instance `gts.example.org` would *not* show that reply in the web view, as it could not determine the preferences of the user from `mastodon.example.org` with regard to showing the "quiet public" post to logged-out visitors to the web page. This could be frustrating for the GtS user, as they might want to show a more complete picture of the thread that they started, right there on their instance; this could also frustrate the Mastodon user, as are used to their "quiet public" posts being visible on the web even when logged out.
|
||||||
|
|
||||||
|
The actor properties `hidesToPublicFromUnauthedWeb` and `hidesCcPublicFromUnauthedWeb` are a move towards solving these issues, by allowing actors to signal their preferences for hiding or showing `to`- and/or `cc`-public posts to unauthenticated visitors via the web.
|
||||||
|
|
||||||
|
For example, the following actor representation indicates that the actor is happy to show both "unlisted" and "public" posts via unauthed web view (this represents the de-facto default for actors on Mastodon and most other server softwares):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://gotosocial.org/ns",
|
||||||
|
"https://www.w3.org/ns/activitystreams"
|
||||||
|
],
|
||||||
|
"type": "Person",
|
||||||
|
[... other properties here ...]
|
||||||
|
"hidesToPublicFromUnauthedWeb": false,
|
||||||
|
"hidesCcPublicFromUnauthedWeb": false,
|
||||||
|
[... other properties here ...]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
By contrast, the following indicates that the actor hides "unlisted" posts but is happy to show "public" posts unauthed (this is the default for actors on GtS instances):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://gotosocial.org/ns",
|
||||||
|
"https://www.w3.org/ns/activitystreams"
|
||||||
|
],
|
||||||
|
"type": "Person",
|
||||||
|
[... other properties here ...]
|
||||||
|
"hidesToPublicFromUnauthedWeb": false,
|
||||||
|
"hidesCcPublicFromUnauthedWeb": true,
|
||||||
|
[... other properties here ...]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And the following shows that the actor wants to show no posts unauthed at all:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://gotosocial.org/ns",
|
||||||
|
"https://www.w3.org/ns/activitystreams"
|
||||||
|
],
|
||||||
|
"type": "Person",
|
||||||
|
[... other properties here ...]
|
||||||
|
"hidesToPublicFromUnauthedWeb": true,
|
||||||
|
"hidesCcPublicFromUnauthedWeb": true,
|
||||||
|
[... other properties here ...]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Both `hidesToPublicFromUnauthedWeb` and `hidesCcPublicFromUnauthedWeb` are defined in [the GoToSocial json-ld `@context` document](https://gotosocial.org/ns).
|
||||||
|
|
||||||
|
In line with its emphasis on having people opt-in to greater visibility rather than opt-out, when receiving a post from a remote actor that does not set these flags, GoToSocial assumes `hidesToPublicFromUnauthedWeb` = `false`, and `hidesCcPublicFromUnauthedWeb` = `true`. That is, the pre-v0.20.x behavior of GoToSocial is still the default for remote servers that don't (yet) use these flags.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
While unusual, it's possible for an actor to also specify that they want to show "unlisted" posts but hide "public" ones:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://gotosocial.org/ns",
|
||||||
|
"https://www.w3.org/ns/activitystreams"
|
||||||
|
],
|
||||||
|
"type": "Person",
|
||||||
|
[... other properties here ...]
|
||||||
|
"hidesToPublicFromUnauthedWeb": true,
|
||||||
|
"hidesCcPublicFromUnauthedWeb": false,
|
||||||
|
[... other properties here ...]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
GoToSocial respects these flags for incoming posts, but it does not let accounts set this combination of flags for outgoing posts. It may be desirable for other implementers to also prevent users from being able to set this state, as it doesn't make a lot of sense.
|
||||||
|
|
||||||
## Actor Migration / Aliasing
|
## Actor Migration / Aliasing
|
||||||
|
|
||||||
GoToSocial supports account migration from one instance/server to another through a combination of the `Move` activity, and the Actor Object properties `alsoKnownAs` and `movedTo`.
|
GoToSocial supports account migration from one instance/server to another through a combination of the `Move` activity, and the Actor Object properties `alsoKnownAs` and `movedTo`.
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ GoToSocial will also sign all outgoing `GET` and `POST` requests that it makes t
|
||||||
|
|
||||||
This behavior is the equivalent of Mastodon's [AUTHORIZED_FETCH / "secure mode"](https://docs.joinmastodon.org/admin/config/#authorized_fetch).
|
This behavior is the equivalent of Mastodon's [AUTHORIZED_FETCH / "secure mode"](https://docs.joinmastodon.org/admin/config/#authorized_fetch).
|
||||||
|
|
||||||
GoToSocial uses the [superseriousbusiness/httpsig](https://github.com/superseriousbusiness/httpsig) library (forked from go-fed) 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-12), 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.)
|
GoToSocial uses the [superseriousbusiness/httpsig](https://codeberg.org/superseriousbusiness/httpsig) library (forked from go-fed) 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-12), 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.)
|
||||||
|
|
||||||
## Query Parameters
|
## Query Parameters
|
||||||
|
|
||||||
|
|
@ -20,11 +20,11 @@ When sending a request, GtS will attempt first *with* query parameters included.
|
||||||
|
|
||||||
When receiving a request, GtS will attempt to validate the signature first *with* query parameters included. If the signature fails to validate, it will reattempt validation without query parameters included.
|
When receiving a request, GtS will attempt to validate the signature first *with* query parameters included. If the signature fails to validate, it will reattempt validation without query parameters included.
|
||||||
|
|
||||||
See [#894](https://github.com/superseriousbusiness/gotosocial/issues/894) for more details.
|
See [#894](https://codeberg.org/superseriousbusiness/gotosocial/issues/894) for more details.
|
||||||
|
|
||||||
## Incoming Requests
|
## Incoming Requests
|
||||||
|
|
||||||
GoToSocial request signature validation is implemented in [internal/federation](https://github.com/superseriousbusiness/gotosocial/blob/main/internal/federation/authenticate.go).
|
GoToSocial request signature validation is implemented in [internal/federation](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/internal/federation/authenticate.go).
|
||||||
|
|
||||||
GoToSocial will attempt to parse the signature using the following algorithms (in order), stopping at the first success:
|
GoToSocial will attempt to parse the signature using the following algorithms (in order), stopping at the first success:
|
||||||
|
|
||||||
|
|
@ -36,9 +36,9 @@ ED25519
|
||||||
|
|
||||||
## Outgoing Requests
|
## Outgoing Requests
|
||||||
|
|
||||||
GoToSocial request signing is implemented in [internal/transport](https://github.com/superseriousbusiness/gotosocial/blob/main/internal/transport/signing.go).
|
GoToSocial request signing is implemented in [internal/transport](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/internal/transport/signing.go).
|
||||||
|
|
||||||
Once https://github.com/superseriousbusiness/gotosocial/issues/2991 is resolved, GoToSocial will use the `(created)` pseudo-header instead of `date`.
|
Once https://codeberg.org/superseriousbusiness/gotosocial/issues/2991 is resolved, GoToSocial will use the `(created)` pseudo-header instead of `date`.
|
||||||
|
|
||||||
For now however, when assembling signatures:
|
For now however, when assembling signatures:
|
||||||
|
|
||||||
|
|
@ -82,4 +82,4 @@ For Mastodon, the public key of a user is served as part of that user's Actor re
|
||||||
|
|
||||||
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.
|
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).
|
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://codeberg.org/superseriousbusiness/gotosocial/issues/1186).
|
||||||
|
|
|
||||||
685
docs/federation/interaction_policy.md
Normal file
685
docs/federation/interaction_policy.md
Normal file
|
|
@ -0,0 +1,685 @@
|
||||||
|
# Interaction Policy
|
||||||
|
|
||||||
|
!!! warning "Feature still in development"
|
||||||
|
Much like GoToSocial, this feature is still in pre-v1 beta development. Naming or schemas or methods of approval / rejection may change. We aim to finalize the document by GoToSocial v0.21.0, at which point it can be considered "ready" for other ActivityPub implementers to use.
|
||||||
|
|
||||||
|
GoToSocial uses the property `interactionPolicy` on posts, in order to indicate to remote instances what sort of interactions are (conditionally) permitted to be processed and stored by the origin server, for any given post.
|
||||||
|
|
||||||
|
The `@context` document for `interactionPolicy` and related objects and properties is at `https://gotosocial.org/ns`.
|
||||||
|
|
||||||
|
!!! danger
|
||||||
|
Interaction policy is an attempt to limit the harmful effects of unwanted replies and other interactions on a user's posts (eg., "reply guys").
|
||||||
|
|
||||||
|
However, it is far from being sufficient for this purpose, as there are still many "out-of-band" ways that posts can be distributed or replied to beyond a user's initial wishes or intentions.
|
||||||
|
|
||||||
|
For example, a user might create a post with a very strict interaction policy attached to it, only to find that other server softwares do not respect that policy, and users on other instances are having discussions and replying to the post *from their instance's perspective*. The original poster's instance will automatically drop these unwanted interactions from their view, but remote instances may still show them.
|
||||||
|
|
||||||
|
Another example: someone might see a post that specifies nobody can reply to it, but screenshot the post, post the screenshot in their own new post, and tag the original post author in as a mention. Alternatively, they might just link to the URL of the post and tag the author in as a mention. In this case, they effectively "reply" to the post by creating a new thread.
|
||||||
|
|
||||||
|
For better or worse, GoToSocial can offer only a best-effort, partial, technological solution to what is more or less an issue of social behavior and boundaries.
|
||||||
|
|
||||||
|
!!! info "Deprecated `always` and `approvalRequired` properties"
|
||||||
|
Previous versions of this document used the properties `always` and `approvalRequired`. These are now deprecated in favor of `automaticApproval` and `manualApproval`. GoToSocial versions before v0.20.0 send and receive only these deprecated properties. GoToSocial v0.20.0 sends and receives both the deprecated and the new properties. GoToSocial v0.21.0 onwards uses only the new properties.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
`interactionPolicy` is an object property attached to the post-like `Object`s `Note`, `Article`, `Question`, etc, with the following format:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://gotosocial.org/ns",
|
||||||
|
[...]
|
||||||
|
],
|
||||||
|
[...],
|
||||||
|
"interactionPolicy": {
|
||||||
|
"canLike": {
|
||||||
|
"automaticApproval": [ "zero_or_more_uris_that_can_always_do_this" ],
|
||||||
|
"manualApproval": [ "zero_or_more_uris_that_require_approval_to_do_this" ]
|
||||||
|
},
|
||||||
|
"canReply": {
|
||||||
|
"automaticApproval": [ "zero_or_more_uris_that_can_always_do_this" ],
|
||||||
|
"manualApproval": [ "zero_or_more_uris_that_require_approval_to_do_this" ]
|
||||||
|
},
|
||||||
|
"canAnnounce": {
|
||||||
|
"automaticApproval": [ "zero_or_more_uris_that_can_always_do_this" ],
|
||||||
|
"manualApproval": [ "zero_or_more_uris_that_require_approval_to_do_this" ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[...]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In the `interactionPolicy` object:
|
||||||
|
|
||||||
|
- `canLike` is a sub-policy which indicates who is permitted to create a `Like` with the post URI as the `object` of the `Like`.
|
||||||
|
- `canReply` is a sub-policy which indicates who is permitted to create a post with `inReplyTo` set to the URI/ID of the post.
|
||||||
|
- `canAnnounce` is a sub-policy which indicates who is permitted to create an `Announce` with the post URI/ID as the `object` of the `Announce`.
|
||||||
|
|
||||||
|
And:
|
||||||
|
|
||||||
|
- `automaticApproval` denotes ActivityPub URIs/IDs of `Actor`s or `Collection`s of `Actor`s who will receive automated approval from the post author to create + distribute an interaction targeting a post.
|
||||||
|
- `manualApproval` denotes ActivityPub URIs/IDs of `Actor`s or `Collection`s of `Actor`s who will receive approval from the post author at the author's own discretion, and may not receive approval at all, or may be rejected (see [Requesting, Obtaining, and Validating Approval](#requesting-obtaining-and-validating-approval)).
|
||||||
|
|
||||||
|
Valid URI entries in `automaticApproval` and `manualApproval` include:
|
||||||
|
|
||||||
|
- the magic ActivityStreams Public URI `https://www.w3.org/ns/activitystreams#Public`
|
||||||
|
- the URIs of the post creator's `Following` and/or `Followers` collections
|
||||||
|
- individual Actor URIs
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public",
|
||||||
|
"https://example.org/users/someone/followers",
|
||||||
|
"https://example.org/users/someone/following",
|
||||||
|
"https://example.org/users/someone_else",
|
||||||
|
"https://somewhere.else.example.org/users/someone_on_a_different_instance"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
Be aware that according to JSON-LD the values of `automaticApproval` and `manualApproval` can be either single strings or arrays of strings. That is, the following are all valid:
|
||||||
|
|
||||||
|
- Single string: `"automaticApproval": "https://example.org/users/someone"`
|
||||||
|
- Single-entry array: `"automaticApproval": [ "https://example.org/users/someone" ]`
|
||||||
|
- Multiple-entry array: `"automaticApproval": [ "https://example.org/users/someone", "https://example.org/users/someone_else" ]`
|
||||||
|
|
||||||
|
## Specifying Nobody
|
||||||
|
|
||||||
|
To specify that **nobody** can perform an interaction on a post **except** for its author (who is always permitted), implementations should set the `automaticApproval` array to **just the URI of the post author**, and `manualApproval` can be unset, `null`, or empty.
|
||||||
|
|
||||||
|
For example, the following `canLike` value indicates that nobody can `Like` the post it is attached to except for the post author:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"canLike": {
|
||||||
|
"automaticApproval": "the_activitypub_uri_of_the_post_author"
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
Another example. The following `interactionPolicy` on a post by `https://example.org/users/someone` indicates that anyone can like the post, but nobody but the author can reply or announce:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://gotosocial.org/ns",
|
||||||
|
[...]
|
||||||
|
],
|
||||||
|
[...],
|
||||||
|
"interactionPolicy": {
|
||||||
|
"canLike": {
|
||||||
|
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
},
|
||||||
|
"canReply": {
|
||||||
|
"automaticApproval": "https://example.org/users/someone"
|
||||||
|
},
|
||||||
|
"canAnnounce": {
|
||||||
|
"automaticApproval": "https://example.org/users/someone"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[...]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
To avoid mischief, GoToSocial makes implicit assumptions about who can/can't interact, even if a policy specifies nobody. See [implicit assumptions](#implicit-assumptions).
|
||||||
|
|
||||||
|
## Conflicting / Duplicate Values
|
||||||
|
|
||||||
|
In cases where a user is present in a Collection URI, and is *also* targeted explicitly by URI, the **more specific value** takes precedence.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[...],
|
||||||
|
"canReply": {
|
||||||
|
"automaticApproval": "https://example.org/users/someone",
|
||||||
|
"manualApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
},
|
||||||
|
[...]
|
||||||
|
```
|
||||||
|
|
||||||
|
Here, `@someone@example.org` is present in `automaticApproval`, and is also implicitly present in the magic ActivityStreams Public collection in `manualApproval`. In this case, they can always reply, as the `automaticApproval` value is more explicit.
|
||||||
|
|
||||||
|
Another example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[...],
|
||||||
|
"canReply": {
|
||||||
|
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public",
|
||||||
|
"manualApproval": "https://example.org/users/someone"
|
||||||
|
},
|
||||||
|
[...]
|
||||||
|
```
|
||||||
|
|
||||||
|
Here, `@someone@example.org` is present in `manualApproval`, but is also implicitly present in the magic ActivityStreams Public collection in `automaticApproval`. In this case everyone can reply without approval, **except** for `@someone@example.org`, who requires approval.
|
||||||
|
|
||||||
|
In case the **exact same** URI is present in both `automaticApproval` and `manualApproval`, the **highest level of permission** takes precedence (ie., a URI in `automaticApproval` takes precedence over the same URI in `manualApproval`).
|
||||||
|
|
||||||
|
## Default / fallback `interactionPolicy`
|
||||||
|
|
||||||
|
When the `interactionPolicy` property is not present at all on a post, or the `interactionPolicy` key is set but its value resolves to `null` or `{}`, implementations can assume the following implicit, default `interactionPolicy` for that post:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://gotosocial.org/ns",
|
||||||
|
[...]
|
||||||
|
],
|
||||||
|
[...],
|
||||||
|
"interactionPolicy": {
|
||||||
|
"canLike": {
|
||||||
|
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
},
|
||||||
|
"canReply": {
|
||||||
|
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
},
|
||||||
|
"canAnnounce": {
|
||||||
|
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[...]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
As implied by the lack of any `manualApproval` property in any of the sub-policies, the default value for `manualApproval` is an empty array.
|
||||||
|
|
||||||
|
This default `interactionPolicy` was designed to reflect the de facto interaction policy of all posts from pre-v0.17.0 GoToSocial, and other ActivityPub server softwares, at the time of writing. That is to say, it is exactly what servers that are not interaction policy aware *already assume* about interaction permissions.
|
||||||
|
|
||||||
|
!!! info "Actors can only ever interact with a post they are permitted to see"
|
||||||
|
Note that even when assuming a default `interactionPolicy` for a post, the **visibility** of a post must still be accounted for by looking at the `to`, `cc`, and/or `audience` properties, to ensure that actors who cannot *see* a post also cannot *interact* with it. Eg., if a post is addressed to followers-only, and the default `interactionPolicy` is assumed, then someone who does not follow the post creator should still *not* be able to see *or* interact with it.
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
As is standard across AP implementations, implementations will likely still wish to limit `Announce` actities targeting the post to only the author themself if the post is addressed to followers-only.
|
||||||
|
|
||||||
|
## Defaults per sub-policy
|
||||||
|
|
||||||
|
When an interaction policy is only *partially* defined (eg., only `canReply` is set, `canLike` or `canAnnounce` keys are not set), then implementations should make the following assumptions for each sub-policy in the `interactionPolicy` object that is *undefined*.
|
||||||
|
|
||||||
|
!!! tip "Future extensions with different defaults"
|
||||||
|
Note that **the below list is not exhaustive**, and extensions to `interactionPolicy` may wish to define **different defaults** for other types of interaction.
|
||||||
|
|
||||||
|
### `canLike`
|
||||||
|
|
||||||
|
If `canLike` is missing on an `interactionPolicy`, or the value of `canLike` is `null` or `{}`, then implementations should assume:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"canLike": {
|
||||||
|
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In other words, the default is **anyone who can see the post can like it**.
|
||||||
|
|
||||||
|
### `canReply`
|
||||||
|
|
||||||
|
If `canReply` is missing on an `interactionPolicy`, or the value of `canReply` is `null` or `{}`, then implementations should assume:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"canReply": {
|
||||||
|
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In other words, the default is **anyone who can see the post can reply to it**.
|
||||||
|
|
||||||
|
### `canAnnounce`
|
||||||
|
|
||||||
|
If `canAnnounce` is missing on an `interactionPolicy`, or the value of `canAnnounce` is `null` or `{}`, then implementations should assume:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"canAnnounce": {
|
||||||
|
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In other words, the default is **anyone who can see the post can announce it**.
|
||||||
|
|
||||||
|
## Indicating that verification is required / not required per sub-policy
|
||||||
|
|
||||||
|
Because not all servers have implemented interaction policies at the time of writing, it is necessary to provide a method by which implementing servers can indicate that they are both **aware of** and **will enforce** interaction policies as described below in the [Interaction Verification](#interaction-verification) section.
|
||||||
|
|
||||||
|
This indication of interaction policy participation is done via a server explicitly setting `interactionPolicy` and its sub-policies on outgoing posts, instead of relying on the defaults described above.
|
||||||
|
|
||||||
|
That is, **by setting `interactionPolicy.*` on a post, an instance indicates to other instances that they will enforce validation of interactions for each sub-policy that is explicitly set.**
|
||||||
|
|
||||||
|
This means that implementations should always explicitly set all sub-policies on an `interactionPolicy` for which they have implemented interaction controls themselves, and with which they would like other servers to comply.
|
||||||
|
|
||||||
|
For example, if a server understands and wishes to enforce the `canLike`, `canReply`, and `canAnnounce` sub-policies (as is the case with GoToSocial), then they should explicitly set those sub-policies on an outgoing post *even when the values do not differ from the implicit defaults*. This allows remote servers to know that the origin server does enforcement, and knows how to handle appropriate `Reject` / `Accept` messages for each sub-policy.
|
||||||
|
|
||||||
|
Another example: if a server only implements the `canReply` interaction sub-policy, but not `canLike` or `canAnnounce`, then they should always set `interactionPolicy.canReply`, and leave the other two sub-policies out of the `interactionPolicy` to indicate that they cannot understand or enforce them.
|
||||||
|
|
||||||
|
This means of indicating participation in interaction policies through the absence of presence of keys was designed so that the large majority of servers that *do not* set `interactionPolicy` at all, because they have not (yet) implemented it, do not need to change their behavior. Servers that do implement `interactionPolicy` can understand, by the absence of the `interactionPolicy` key on a post, that the origin server is not `interactionPolicy` aware, and behave accordingly.
|
||||||
|
|
||||||
|
## Implicit Assumptions
|
||||||
|
|
||||||
|
For common-sense safety reasons, GoToSocial makes, and will always apply, two implicit assumptions about interaction policies.
|
||||||
|
|
||||||
|
### 1. Mentioned + replied-to actors can always reply
|
||||||
|
|
||||||
|
Actors mentioned in, or replied to by, a post should **ALWAYS** be able to reply to that post without requiring approval, regardless of the post visiblity and the `interactionPolicy`, **UNLESS** the post that mentioned or replied to them is itself currently pending approval.
|
||||||
|
|
||||||
|
This is to prevent a would-be harasser from mentioning someone in an abusive post, and leaving no recourse to the mentioned user to reply.
|
||||||
|
|
||||||
|
As such, when sending out interaction policies, GoToSocial will **ALWAYS** add the URIs of mentioned users to the `canReply.always` array, unless they are already covered by the ActivityStreams magic public URI.
|
||||||
|
|
||||||
|
Likewise, when enforcing received interaction policies, GoToSocial will **ALWAYS** behave as though the URIs of mentioned users were present in the `canReply.always` array, even if they weren't.
|
||||||
|
|
||||||
|
### 2. An actor can always interact in any way with their own post
|
||||||
|
|
||||||
|
**Secondly**, an actor should **ALWAYS** be able to reply to their own post, like their own post, and boost their own post without requiring approval, **UNLESS** that post is itself currently pending approval.
|
||||||
|
|
||||||
|
As such, when sending out interaction policies, GoToSocial will **ALWAYS** add the URI of the post author to the `canLike.always`, `canReply.always`, and `canAnnounce.always` arrays, **UNLESS** they are already covered by the ActivityStreams magic public URI.
|
||||||
|
|
||||||
|
Likewise, when enforcing received interaction policies, GoToSocial will **ALWAYS** behave as though the URI of the post author themself is present in each `automaticApproval` field, even if it wasn't.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Here's some examples of what interaction policies allow users to do.
|
||||||
|
|
||||||
|
### 1. Limiting scope of a conversation
|
||||||
|
|
||||||
|
In this example, the user `@the_mighty_zork` wants to begin a conversation with the users `@booblover6969` and `@hodor`.
|
||||||
|
|
||||||
|
To avoid the discussion being derailed by others, they want replies to their post by users other than the three participants to be permitted only if they're approved by `@the_mighty_zork`.
|
||||||
|
|
||||||
|
Furthermore, they want to limit the boosting / `Announce`ing of their post to only their own followers, and to the three conversation participants.
|
||||||
|
|
||||||
|
However, anyone should be able to `Like` the post by `@the_mighty_zork`.
|
||||||
|
|
||||||
|
This can be achieved with the following `interactionPolicy`, which is attached to a post with visibility level public:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://gotosocial.org/ns",
|
||||||
|
[...]
|
||||||
|
],
|
||||||
|
[...],
|
||||||
|
"interactionPolicy": {
|
||||||
|
"canLike": {
|
||||||
|
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
},
|
||||||
|
"canReply": {
|
||||||
|
"automaticApproval": [
|
||||||
|
"https://example.org/users/the_mighty_zork",
|
||||||
|
"https://example.org/users/booblover6969",
|
||||||
|
"https://example.org/users/hodor"
|
||||||
|
],
|
||||||
|
"manualApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
},
|
||||||
|
"canAnnounce": {
|
||||||
|
"automaticApproval": [
|
||||||
|
"https://example.org/users/the_mighty_zork",
|
||||||
|
"https://example.org/users/the_mighty_zork/followers",
|
||||||
|
"https://example.org/users/booblover6969",
|
||||||
|
"https://example.org/users/hodor"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[...]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Long solo thread
|
||||||
|
|
||||||
|
In this example, the user `@the_mighty_zork` wants to write a long solo thread.
|
||||||
|
|
||||||
|
They don't mind if people boost and like posts in the thread, but they don't want to get any replies because they don't have the energy to moderate the discussion; they just want to vent by throwing their thoughts out there.
|
||||||
|
|
||||||
|
This can be achieved by setting the following `interactionPolicy` on every post in the thread:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://gotosocial.org/ns",
|
||||||
|
[...]
|
||||||
|
],
|
||||||
|
[...],
|
||||||
|
"interactionPolicy": {
|
||||||
|
"canLike": {
|
||||||
|
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
},
|
||||||
|
"canReply": {
|
||||||
|
"automaticApproval": "https://example.org/users/the_mighty_zork"
|
||||||
|
},
|
||||||
|
"canAnnounce": {
|
||||||
|
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[...]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Here, anyone is allowed to like or boost, but nobody is permitted to reply (except `@the_mighty_zork` themself).
|
||||||
|
|
||||||
|
### 3. Completely open
|
||||||
|
|
||||||
|
In this example, `@the_mighty_zork` wants to write a completely open post that can be replied to, boosted, or liked by anyone who can see it:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://gotosocial.org/ns",
|
||||||
|
[...]
|
||||||
|
],
|
||||||
|
[...],
|
||||||
|
"interactionPolicy": {
|
||||||
|
"canLike": {
|
||||||
|
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
},
|
||||||
|
"canReply": {
|
||||||
|
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
},
|
||||||
|
"canAnnounce": {
|
||||||
|
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[...]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Subsequent Replies / Scope Widening
|
||||||
|
|
||||||
|
Each subsequent reply in a conversation will have its own interaction policy, chosen by the user who created the reply. In other words, the entire *conversation* or *thread* is not controlled by one `interactionPolicy`, but the policy can differ for each subsequent post in a thread, as set by the post author.
|
||||||
|
|
||||||
|
Unfortunately, this means that even with `interactionPolicy` in place, the scope of a thread can inadvertently widen beyond the intention of the author of the first post in the thread.
|
||||||
|
|
||||||
|
For instance, in [example 1](#example-1---limiting-scope-of-a-conversation) above, `@the_mighty_zork` specifies in the first post a `canReply.always` value of
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
"https://example.org/users/the_mighty_zork",
|
||||||
|
"https://example.org/users/booblover6969",
|
||||||
|
"https://example.org/users/hodor"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
In a subsequent reply, either accidentally or on purpose `@booblover6969` sets the `canReply.always` value to:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
This widens the scope of the conversation, as now anyone can reply to `@booblover6969`'s post, and possibly also tag `@the_mighty_zork` in that reply.
|
||||||
|
|
||||||
|
To avoid this issue, it is recommended that remote instances prevent users from being able to widen scope (exact mechanism of doing this TBD).
|
||||||
|
|
||||||
|
It is also a good idea for instances to consider any interaction with a post-like `Object` that is itself currently pending approval, as also pending approval.
|
||||||
|
|
||||||
|
In other words, instances should mark all children interactions below a pending-approval parent as also pending approval, no matter what the interaction policy on the parent would ordinarily allow.
|
||||||
|
|
||||||
|
This avoids situations where someone could reply to a post, then, even if their reply is pending approval, they could reply *to their own reply* and have that marked as permitted (since as author, they would normally have [implicit permission to reply](#implicit-assumptions)).
|
||||||
|
|
||||||
|
## Interaction Verification
|
||||||
|
|
||||||
|
The [interaction policy](#interaction-policy) section described the shape of interaction policies, assumed defaults, and assumptions.
|
||||||
|
|
||||||
|
This section describes the enforcement and verification of interaction policies, ie., how servers that set interaction policies should send approval or rejection of a requested/pending interaction, and how other servers can prove that approval to interact with a post has been obtained by the interacter from the interactee.
|
||||||
|
|
||||||
|
### Requesting, Obtaining, and Validating Approval
|
||||||
|
|
||||||
|
When an actor's URI is in the `manualApproval` array for a type of interaction, **or** their presence in a collection needs to be validated (see [Validating presence in a Followers / Following collection](#validating-presence-in-a-followers--following-collection)), implementations wishing to obtain approval for that actor to interact with a policied post should do the following:
|
||||||
|
|
||||||
|
1. Compose the interaction `Activity` (ie., `Like`, `Create` (reply), or `Announce`), as normal.
|
||||||
|
2. Address the `Activity` `to` and `cc` the expected recipients for that `Activity`, as normal.
|
||||||
|
3. `POST` the `Activity` only to the `Inbox` (or `sharedInbox`) of the author of the post being interacted with.
|
||||||
|
4. **DO NOT DISTRIBUTE THE ACTIVITY FURTHER THAN THIS AT THIS POINT**.
|
||||||
|
|
||||||
|
At this point, the interaction can be considered as *pending approval*, and should not be shown in the replies or likes collections, etc., of the post interacted with.
|
||||||
|
|
||||||
|
It may be shown to the user who sent the interaction as a sort of "interaction pending" modal, but ideally it should not be shown to other users who share an instance with that user.
|
||||||
|
|
||||||
|
From here, one of three things may happen:
|
||||||
|
|
||||||
|
#### Rejection
|
||||||
|
|
||||||
|
In this scenario, the server of the author of the post being interacted with sends back a `Reject` `Activity` with the interaction URI/ID as the `object` property.
|
||||||
|
|
||||||
|
For example, the following json object `Reject`s the attempt of `@someone@somewhere.else.example.org` to reply to a post by `@post_author@example.org`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
|
"actor": "https://example.org/users/post_author",
|
||||||
|
"to": "https://somewhere.else.example.org/users/someone",
|
||||||
|
"id": "https://example.org/users/post_author/activities/reject/01J0K2YXP9QCT5BE1JWQSAM3B6",
|
||||||
|
"object": "https://somewhere.else.example.org/users/someone/statuses/01J17XY2VXGMNNPH1XR7BG2524",
|
||||||
|
"type": "Reject"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If this happens, `@someone@somewhere.else.example.org` (and their instance) should consider the interaction as having been rejected. The instance should delete the activity from its internal storage (ie., database), or otherwise indicate that it's been rejected, and it should not distribute the `Activity` further, or retry the interaction. The server may wish to indicate to the interacter that their interaction was rejected.
|
||||||
|
|
||||||
|
#### Nothing
|
||||||
|
|
||||||
|
In this scenario, the author of the post being interacted with never sends back a `Reject` or an `Accept` `Activity`. In such a case, the interaction is considered "pending" in perpetuity. Instances may wish to implement some kind of cleanup feature, where sent and pending interactions that reach a certain age should be considered expired, and `Rejected` and then removed in the manner gestured towards above.
|
||||||
|
|
||||||
|
#### Acceptance
|
||||||
|
|
||||||
|
In this scenario, the author of the post being interacted with sends back an `Accept` `Activity` with the interaction URI/ID as the `object` property, and a dereferenceable URI of an approval object as the `result` property (see [Approval Objects](#approval-objects)).
|
||||||
|
|
||||||
|
For example, the following json object `Accept`s the attempt of `@someone@somewhere.else.example.org` to reply to a post by `@post_author@example.org`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
|
"actor": "https://example.org/users/post_author",
|
||||||
|
"cc": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public",
|
||||||
|
"https://example.org/users/post_author/followers"
|
||||||
|
],
|
||||||
|
"to": "https://somewhere.else.example.org/users/someone",
|
||||||
|
"id": "https://example.org/users/post_author/activities/accept/01J0K2YXP9QCT5BE1JWQSAM3B6",
|
||||||
|
"object": "https://somewhere.else.example.org/users/someone/statuses/01J17XY2VXGMNNPH1XR7BG2524",
|
||||||
|
"result": "https://example.org/users/post_author/reply_approvals/01JMMGABRDNA9G9BDNYJR7TC8D",
|
||||||
|
"type": "Accept"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If this happens, `@someone@somewhere.else.example.org` (and their instance) should consider the interaction as having been approved/accepted by the interactee.
|
||||||
|
|
||||||
|
At this point, `somewhere.else.example.org` should once again send out the interaction, with the following differences:
|
||||||
|
|
||||||
|
1. This time, include the `result` URI/ID from the `accept` in the `approvedBy` field of the post contained in the `Create` (see [`approvedBy`](#approvedby)).
|
||||||
|
2. This time, distribute the interaction to **all** of the recipients targed by `to`, `cc`, etc.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
While it is not strictly necessary, in the above example, actor `https://example.org/users/post_author` addresses the `Accept` activity not just to the interacting actor `https://somewhere.else.example.org/users/someone`, but to their followers collection as well (and, implicitly, to the public). This allows followers of `https://example.org/users/post_author` on other servers to also mark the interaction as accepted, and to show the interaction alongside the interacted-with post, without having to dereference + verify the URI in `approvedBy`.
|
||||||
|
|
||||||
|
### Approval Objects
|
||||||
|
|
||||||
|
An approval is an extension of a basic ActivityStreams Object, with the type `LikeApproval`, `ReplyApproval`, or `AnnounceApproval`. Each type corresponds to the type of interaction that the particular approval approves.
|
||||||
|
|
||||||
|
`LikeApproval`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://gotosocial.org/ns"
|
||||||
|
],
|
||||||
|
"attributedTo": "https://example.org/users/post_author",
|
||||||
|
"id": "https://example.org/users/post_author/approvals/01J0K2YXP9QCT5BE1JWQSAM3B6",
|
||||||
|
"object": "https://somewhere.else.example.org/users/someone/likes/01JMPKG79EAH0NB04BHEM9D20N",
|
||||||
|
"target": "https://example.org/users/post_author/statuses/01JJYV141Y5M4S65SC1XCP65NT",
|
||||||
|
"type": "LikeApproval"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`ReplyApproval`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://gotosocial.org/ns"
|
||||||
|
],
|
||||||
|
"attributedTo": "https://example.org/users/post_author",
|
||||||
|
"id": "https://example.org/users/post_author/approvals/01J0K2YXP9QCT5BE1JWQSAM3B6",
|
||||||
|
"object": "https://somewhere.else.example.org/users/someone/statuses/01J17XY2VXGMNNPH1XR7BG2524",
|
||||||
|
"target": "https://example.org/users/post_author/statuses/01JJYV141Y5M4S65SC1XCP65NT",
|
||||||
|
"type": "ReplyApproval"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`AnnounceApproval`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://gotosocial.org/ns"
|
||||||
|
],
|
||||||
|
"attributedTo": "https://example.org/users/post_author",
|
||||||
|
"id": "https://example.org/users/post_author/approvals/01J0K2YXP9QCT5BE1JWQSAM3B6",
|
||||||
|
"object": "https://somewhere.else.example.org/users/someone/boosts/01JMPKG79EAH0NB04BHEM9D20N",
|
||||||
|
"target": "https://example.org/users/post_author/statuses/01JJYV141Y5M4S65SC1XCP65NT",
|
||||||
|
"type": "AnnounceApproval"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In an approval object:
|
||||||
|
|
||||||
|
- `attributedTo`: should be the `Actor` who `Accept`ed an interaction, ie., the *interactee*.
|
||||||
|
- `object`: should be the *interacting* `Like`, `Announce`, or post-like `Object`.
|
||||||
|
- `target` (optional): if included, should be the post-like `Object` that was *interacted with*.
|
||||||
|
|
||||||
|
!!! info "Approval objects should be dereferenceable"
|
||||||
|
As a consequence of the validation mechanism (see [Validating `approvedBy`](#validating-approvedby)), instances should make sure that they serve a valid ActivityPub response to dereferences of approval object URIs. If they do not, they inadvertently risk restricting the ability of remote instances to distribute their posts.
|
||||||
|
|
||||||
|
### `approvedBy`
|
||||||
|
|
||||||
|
`approvedBy` is an additional property added to the `Like`, and `Announce` activities, and any `Object`s considered to be "posts" (`Note`, `Article`, etc).
|
||||||
|
|
||||||
|
The presence of `approvedBy` signals that the author of the post targeted by the `Activity` or replied-to by the `Object` has approved/accepted the interaction, and it can now be distributed to its intended audience.
|
||||||
|
|
||||||
|
The value of `approvedBy` should be the `result` URI/ID that was sent along in an `Accept` from the interactee, which points towards a dereferenceable approval object.
|
||||||
|
|
||||||
|
For example, the following `Announce` `Activity` claims, by the presence of `approvedBy`, that it has been `Accept`ed by `@post_author@example.org`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"actor": "https://somewhere.else.example.org/users/someone",
|
||||||
|
"to": [
|
||||||
|
"https://somewhere.else.example.org/users/someone/followers"
|
||||||
|
],
|
||||||
|
"cc": [
|
||||||
|
"https://example.org/users/post_author"
|
||||||
|
],
|
||||||
|
"id": "https://somewhere.else.example.org/users/someone/activities/announce/01J0K2YXP9QCT5BE1JWQSAM3B6",
|
||||||
|
"object": "https://example.org/users/post_author/statuses/01J17ZZFK6W82K9MJ9SYQ33Y3D",
|
||||||
|
"approvedBy": "https://example.org/users/post_author/reply_approvals/01JMMGABRDNA9G9BDNYJR7TC8D",
|
||||||
|
"type": "Announce"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Validating `approvedBy`
|
||||||
|
|
||||||
|
When receiving an `Activity` or a post-like `Object` with an `approvedBy` value attached to it, remote instances should:
|
||||||
|
|
||||||
|
1. Validate that the host/domain of the `approvedBy` URI is equal to the host/domain of the author of the post being interacted with.
|
||||||
|
2. Dereference the `approvedBy` URI/ID to get the approval object (see [Approval Objects](#approval-objects)).
|
||||||
|
3. Check the type of the approval to ensure it's the correct one, eg., an `Announce` should have an `approvedBy` URI/ID set on it that points to an `AnnounceApproval`, not a `ReplyApproval` or a `LikeApproval`.
|
||||||
|
4. Check that the approval has an `attributedTo` value equal to the URI/ID of the actor being interacted with.
|
||||||
|
5. Check that the approval has an `object` value equal to the `id` of the interaction `Activity` or `Object`.
|
||||||
|
|
||||||
|
If the approval cannot be dereferenced, or does not pass validity checks, the interaction should be considered invalid and dropped.
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
Versions 0.17.x and 0.18.x of GoToSocial did not include a `result` pointing towards an approval type. Instead, the URI/ID of the `Accept` was sent in the `approvedBy` field.
|
||||||
|
|
||||||
|
Version 0.18.x of GoToSocial is partially forward-compatible with approval types, since it can validate approval using either a dereferenced `Accept` or a dereferenced approval type, while still sending out an `Accept` URI itself in the `approvedBy` field.
|
||||||
|
|
||||||
|
Versions of GoToSocial from 0.19.x upwards will send out an `approvedBy` pointing to an approval type, as described in this document, not an `Accept`.
|
||||||
|
|
||||||
|
### Validating presence in a Followers / Following collection
|
||||||
|
|
||||||
|
If an `Actor` interacting with an `object` (via `Like`, `inReplyTo`, or `Announce`) is permitted to do that interaction based on their presence in a `Followers` or `Following` collection in the `automaticApproval` field of an interaction policy, then their server **should still wait** for an `Accept` to be received from the server of the target actor, before distributing the interaction more widely with the `approvedBy` property set to the URI/ID of the approval.
|
||||||
|
|
||||||
|
This is to prevent scenarios where third servers have to somehow verify the presence of the interacting `Actor` in the `Followers` or `Following` collection of the `Actor` being interacted with. It is simpler to allow the target server to do that verification, and to trust that their approval implicitly agrees that the interacting `Actor` is present in the relevant collection.
|
||||||
|
|
||||||
|
Likewise, when receiving an interaction from an `Actor` whose permission to interact matches with one of the `Following` or `Followers` collections in the `automaticApproval` property, the server of the interacted-with `Actor` should ensure that they *always* send out an `Accept` as soon as possible, so that the interacting `Actor` server can send out the `Activity` with the proper proof of acceptance.
|
||||||
|
|
||||||
|
This process should bypass the normal "pending approval" stage whereby the server of the `Actor` being interacted with notifies them of the pending interaction, and waits for them to accept or reject, since there is no point notifying an `Actor` of a pending approval that they have already explicitly agreed to. In the GoToSocial codebase in particular, this is called "preapproval".
|
||||||
|
|
||||||
|
### Optional behaviors
|
||||||
|
|
||||||
|
This section describes optional behaviors that implementers *may* use when sending `Accept` and `Reject` messages, and *should* account for when receiving `Accept` and `Reject` messages.
|
||||||
|
|
||||||
|
#### Always send out `Accept`s
|
||||||
|
|
||||||
|
Implementers may wish to *always* send out an `Accept` to remote interacters, even when the interaction is implicitly or explicitly permitted by their presence in the `automaticApproval` array. When receiving such an `Accept`, implementations may still want to update their representation of the interaction to include an `approvedBy` URI pointing at an approval. This may be useful later on when handling revocations (TODO).
|
||||||
|
|
||||||
|
#### Type hinting: inline `object` property on `Accept` and `Reject`
|
||||||
|
|
||||||
|
If desired, implementers may partially expand/inline the `object` property of an `Accept` or `Reject` to hint to remote servers about the type of interaction being `Accept`ed or `Reject`ed. When inlining in this way, the `object`'s `type` and `id` must be defined at a minimum. For example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
|
"actor": "https://example.org/users/post_author",
|
||||||
|
"cc": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public",
|
||||||
|
"https://example.org/users/post_author/followers"
|
||||||
|
],
|
||||||
|
"to": "https://somewhere.else.example.org/users/someone",
|
||||||
|
"id": "https://example.org/users/post_author/activities/reject/01J0K2YXP9QCT5BE1JWQSAM3B6",
|
||||||
|
"object": {
|
||||||
|
"type": "Note",
|
||||||
|
"id": "https://somewhere.else.example.org/users/someone/statuses/01J17XY2VXGMNNPH1XR7BG2524",
|
||||||
|
[...]
|
||||||
|
},
|
||||||
|
"result": "https://example.org/users/post_author/approvals/01JMPS01E54DG9JCF2ZK3JDMXE",
|
||||||
|
"type": "Accept"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Set `target` property on `Accept` and `Reject`
|
||||||
|
|
||||||
|
If desired, implementers may set the `target` property on outgoing `Accept` or `Reject` activities to the `id` of the post being interacted with, to make it easier for remote servers to understand the shape and relevance of the interaction that's being `Accept`ed or `Reject`ed.
|
||||||
|
|
||||||
|
For example, the following json object `Accept`s the attempt of `@someone@somewhere.else.example.org` to reply to a post by `@post_author@example.org` that has the id `https://example.org/users/post_author/statuses/01JJYV141Y5M4S65SC1XCP65NT`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
|
"actor": "https://example.org/users/post_author",
|
||||||
|
"cc": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public",
|
||||||
|
"https://example.org/users/post_author/followers"
|
||||||
|
],
|
||||||
|
"to": "https://somewhere.else.example.org/users/someone",
|
||||||
|
"id": "https://example.org/users/post_author/activities/reject/01J0K2YXP9QCT5BE1JWQSAM3B6",
|
||||||
|
"object": "https://somewhere.else.example.org/users/someone/statuses/01J17XY2VXGMNNPH1XR7BG2524",
|
||||||
|
"target": "https://example.org/users/post_author/statuses/01JJYV141Y5M4S65SC1XCP65NT",
|
||||||
|
"result": "https://example.org/users/post_author/approvals/01JMPS01E54DG9JCF2ZK3JDMXE",
|
||||||
|
"type": "Accept"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If desired, the `target` property can also be partially expanded/inlined to type hint about the post that was interacted with. When inlining in this way, the `target`'s `type` and `id` must be defined at a minimum. For example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
|
"actor": "https://example.org/users/post_author",
|
||||||
|
"cc": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public",
|
||||||
|
"https://example.org/users/post_author/followers"
|
||||||
|
],
|
||||||
|
"to": "https://somewhere.else.example.org/users/someone",
|
||||||
|
"id": "https://example.org/users/post_author/activities/reject/01J0K2YXP9QCT5BE1JWQSAM3B6",
|
||||||
|
"object": "https://somewhere.else.example.org/users/someone/statuses/01J17XY2VXGMNNPH1XR7BG2524",
|
||||||
|
"target": {
|
||||||
|
"type": "Note",
|
||||||
|
"id": "https://example.org/users/post_author/statuses/01JJYV141Y5M4S65SC1XCP65NT"
|
||||||
|
[ ... ]
|
||||||
|
},
|
||||||
|
"result": "https://example.org/users/post_author/approvals/01JMPS01E54DG9JCF2ZK3JDMXE",
|
||||||
|
"type": "Accept"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
@ -1,5 +1,56 @@
|
||||||
# Posts and Post Properties
|
# Posts and Post Properties
|
||||||
|
|
||||||
|
## Attachments, Blurhash, and Focal Point
|
||||||
|
|
||||||
|
GoToSocial sends media attachments in the `attachment` property of posts using the following types:
|
||||||
|
|
||||||
|
- `Image` - any image type (webp, jpeg, gif, png, etc).
|
||||||
|
- `Video` - any video type (mp4, mkv, webm, etc).
|
||||||
|
- `Audio` - any audio type (mp3, flac, wma, etc).
|
||||||
|
- `Document` - fallback for any other / unknown type.
|
||||||
|
|
||||||
|
Attachments sent from GoToSocial include the MIME `mediaType`, the `url` of the full-sized version of the media file, and the `summary` property, which can be interpreted by remote instance's as a short description / alt text for the attachment.
|
||||||
|
|
||||||
|
Types `Image` and `Video` will also include the `http://joinmastodon.org/ns#blurhash` property so that remotes can generate a colorful hash of the image. If an audio file included an embedded cover art image, then the `Audio` type will also include a blurhash. See the [Mastodon blurhash docs](https://docs.joinmastodon.org/spec/activitypub/#blurhash).
|
||||||
|
|
||||||
|
`Image` types may also include the `http://joinmastodon.org/ns#focalPoint` property, which will be an array of two floats between -1.0 and 1.0 indicating the x-y coordinates of the image's focal point. See the [Mastondon focalPoint docs](https://docs.joinmastodon.org/spec/activitypub/#focalPoint).
|
||||||
|
|
||||||
|
Here's an example of a `Note` with one attachment:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
{
|
||||||
|
"blurhash": "toot:blurhash",
|
||||||
|
"focalPoint": {
|
||||||
|
"@container": "@list",
|
||||||
|
"@id": "toot:focalPoint"
|
||||||
|
},
|
||||||
|
"toot": "http://joinmastodon.org/ns#"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "Note",
|
||||||
|
[...],
|
||||||
|
"attachment": [
|
||||||
|
{
|
||||||
|
"blurhash": "LIIE|gRj00WB-;j[t7j[4nWBj[Rj",
|
||||||
|
"focalPoint": [
|
||||||
|
-0.5,
|
||||||
|
0.5
|
||||||
|
],
|
||||||
|
"mediaType": "image/jpeg",
|
||||||
|
"summary": "Black and white image of some 50's style text saying: Welcome On Board",
|
||||||
|
"type": "Image",
|
||||||
|
"url": "http://localhost:8080/fileserver/01F8MH17FWEB39HZJ76B6VXSKF/attachment/original/01F8MH6NEM8D7527KZAECTCR76.jpg"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[...]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
When receiving posts with attachments from remote instances, it will try to parse any of the four types `Image`, `Video`, `Audio`, or `Document` into media attachments. It doesn't matter which type is used. It will check for `blurhash` and `focalPoint` properties and use these if they are set. It will use the `summary` value as a short description / alt text, falling back to `name` if `summary` is not set.
|
||||||
|
|
||||||
## Hashtags
|
## 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.
|
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.
|
||||||
|
|
@ -219,530 +270,9 @@ If `contentMap` has multiple entries, we have no way of determining the intended
|
||||||
|
|
||||||
## Interaction Policy
|
## Interaction Policy
|
||||||
|
|
||||||
GoToSocial uses the property `interactionPolicy` on posts in order to indicate to remote instances what sort of interactions will be (conditionally) permitted for any given post.
|
GoToSocial uses the property `interactionPolicy` on posts, in order to indicate to remote instances what sort of interactions are (conditionally) permitted to be processed and stored by the origin server, for any given post.
|
||||||
|
|
||||||
!!! danger
|
For more details on this, see the separate [interaction policy](./interaction_policy.md) document.
|
||||||
|
|
||||||
Interaction policy is an attempt to limit the harmful effects of unwanted replies and other interactions on a user's posts (eg., "reply guys").
|
|
||||||
|
|
||||||
However, it is far from being sufficient for this purpose, as there are still many "out-of-band" ways that posts can be distributed or replied to beyond a user's initial wishes or intentions.
|
|
||||||
|
|
||||||
For example, a user might create a post with a very strict interaction policy attached to it, only to find that other server softwares do not respect that policy, and users on other instances are having discussions and replying to the post *from their instance's perspective*. The original poster's instance will automatically drop these unwanted interactions from their view, but remote instances may still show them.
|
|
||||||
|
|
||||||
Another example: someone might see a post that specifies nobody can reply to it, but screenshot the post, post the screenshot in their own new post, and tag the original post author in as a mention. Alternatively, they might just link to the URL of the post and tag the author in as a mention. In this case, they effectively "reply" to the post by creating a new thread.
|
|
||||||
|
|
||||||
For better or worse, GoToSocial can offer only a best-effort, partial, technological solution to what is more or less an issue of social behavior and boundaries.
|
|
||||||
|
|
||||||
### Overview
|
|
||||||
|
|
||||||
`interactionPolicy` is a property attached to the status-like `Object`s `Note`, `Article`, `Question`, etc, with the following format:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
[...],
|
|
||||||
"interactionPolicy": {
|
|
||||||
"canLike": {
|
|
||||||
"always": [ "zero_or_more_uris_that_can_always_do_this" ],
|
|
||||||
"approvalRequired": [ "zero_or_more_uris_that_require_approval_to_do_this" ]
|
|
||||||
},
|
|
||||||
"canReply": {
|
|
||||||
"always": [ "zero_or_more_uris_that_can_always_do_this" ],
|
|
||||||
"approvalRequired": [ "zero_or_more_uris_that_require_approval_to_do_this" ]
|
|
||||||
},
|
|
||||||
"canAnnounce": {
|
|
||||||
"always": [ "zero_or_more_uris_that_can_always_do_this" ],
|
|
||||||
"approvalRequired": [ "zero_or_more_uris_that_require_approval_to_do_this" ]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[...]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
In this object:
|
|
||||||
|
|
||||||
- `canLike` indicates who can create a `Like` with the post URI as the `Object` of the `Like`.
|
|
||||||
- `canReply` indicates who can create a post with `inReplyTo` set to the URI of the post.
|
|
||||||
- `canAnnounce` indicates who can create an `Announce` with the post URI as the `Object` of the `Announce`.
|
|
||||||
|
|
||||||
And:
|
|
||||||
|
|
||||||
- `always` is an array of ActivityPub URIs/IDs of `Actor`s or `Collection`s of `Actor`s who do not require an `Accept` in order to distribute an interaction to their followers (more on this below).
|
|
||||||
- `approvalRequired` is an array of ActivityPub URIs/IDs of `Actor`s or `Collection`s of `Actor`s who can interact, but should wait for an `Accept` before distributing an interaction to their followers.
|
|
||||||
|
|
||||||
Valid URI entries in `always` and `approvalRequired` include the magic ActivityStreams Public URI `https://www.w3.org/ns/activitystreams#Public`, the URIs of the post creator's `Following` and/or `Followers` collections, and individual Actor URIs. For example:
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
"https://www.w3.org/ns/activitystreams#Public",
|
|
||||||
"https://example.org/users/someone/followers",
|
|
||||||
"https://example.org/users/someone/following",
|
|
||||||
"https://example.org/users/someone_else",
|
|
||||||
"https://somewhere.else.example.org/users/someone_on_a_different_instance"
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Specifying Nobody
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
GoToSocial makes implicit assumptions about who can/can't interact, even if a policy specifies nobody. See [implicit assumptions](#implicit-assumptions).
|
|
||||||
|
|
||||||
An empty array, or a missing or null key, indicates that nobody can do the interaction.
|
|
||||||
|
|
||||||
For example, the following `canLike` value indicates that nobody can `Like` the post:
|
|
||||||
|
|
||||||
```json
|
|
||||||
"canLike": {
|
|
||||||
"always": [],
|
|
||||||
"approvalRequired": []
|
|
||||||
},
|
|
||||||
```
|
|
||||||
|
|
||||||
Likewise, a `canLike` value of `null` also indicates that nobody can `Like` the post:
|
|
||||||
|
|
||||||
```json
|
|
||||||
"canLike": null
|
|
||||||
```
|
|
||||||
|
|
||||||
or
|
|
||||||
|
|
||||||
```json
|
|
||||||
"canLike": {
|
|
||||||
"always": null,
|
|
||||||
"approvalRequired": null
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
And a missing `canLike` value does the same thing:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
[...],
|
|
||||||
"interactionPolicy": {
|
|
||||||
"canReply": {
|
|
||||||
"always": [ "zero_or_more_uris_that_can_always_do_this" ],
|
|
||||||
"approvalRequired": [ "zero_or_more_uris_that_require_approval_to_do_this" ]
|
|
||||||
},
|
|
||||||
"canAnnounce": {
|
|
||||||
"always": [ "zero_or_more_uris_that_can_always_do_this" ],
|
|
||||||
"approvalRequired": [ "zero_or_more_uris_that_require_approval_to_do_this" ]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[...]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Conflicting / Duplicate Values
|
|
||||||
|
|
||||||
In cases where a user is present in a Collection URI, and is *also* targeted explicitly by URI, the **more specific value** takes precedence.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
```json
|
|
||||||
[...],
|
|
||||||
"canReply": {
|
|
||||||
"always": [
|
|
||||||
"https://example.org/users/someone"
|
|
||||||
],
|
|
||||||
"approvalRequired": [
|
|
||||||
"https://www.w3.org/ns/activitystreams#Public"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
[...]
|
|
||||||
```
|
|
||||||
|
|
||||||
Here, `@someone@example.org` is present in the `always` array, and is also implicitly present in the magic ActivityStreams Public collection in the `approvalRequired` array. In this case, they can always reply, as the `always` value is more explicit.
|
|
||||||
|
|
||||||
Another example:
|
|
||||||
|
|
||||||
```json
|
|
||||||
[...],
|
|
||||||
"canReply": {
|
|
||||||
"always": [
|
|
||||||
"https://www.w3.org/ns/activitystreams#Public"
|
|
||||||
],
|
|
||||||
"approvalRequired": [
|
|
||||||
"https://example.org/users/someone"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
[...]
|
|
||||||
```
|
|
||||||
|
|
||||||
Here, `@someone@example.org` is present in the `approvalRequired` array, but is also implicitly present in the magic ActivityStreams Public collection in the `always` array. In this case everyone can reply without approval, **except** for `@someone@example.org`, who requires approval.
|
|
||||||
|
|
||||||
In case the **exact same** URI is present in both `always` and `approvalRequired`, the **highest level of permission** takes precedence (ie., a URI in `always` takes precedence over the same URI in `approvalRequired`).
|
|
||||||
|
|
||||||
### Implicit Assumptions
|
|
||||||
|
|
||||||
GoToSocial makes several implicit assumptions about `interactionPolicy`s.
|
|
||||||
|
|
||||||
**Firstly**, users [mentioned](#mentions) in, or replied to by, a post should **ALWAYS** be able to reply to that post without requiring approval, regardless of the post visiblity and the `interactionPolicy`, **UNLESS** the post that mentioned or replied to them is itself currently pending approval.
|
|
||||||
|
|
||||||
This is to prevent a would-be harasser from mentioning someone in an abusive post, and leaving no recourse to the mentioned user to reply.
|
|
||||||
|
|
||||||
As such, when sending out interaction policies, GoToSocial will **ALWAYS** add the URIs of mentioned users to the `canReply.always` array, unless they are already covered by the ActivityStreams magic public URI.
|
|
||||||
|
|
||||||
Likewise, when enforcing received interaction policies, GoToSocial will **ALWAYS** behave as though the URIs of mentioned users were present in the `canReply.always` array, even if they weren't.
|
|
||||||
|
|
||||||
**Secondly**, a user should **ALWAYS** be able to reply to their own post, like their own post, and boost their own post without requiring approval, **UNLESS** that post is itself currently pending approval.
|
|
||||||
|
|
||||||
As such, when sending out interaction policies, GoToSocial will **ALWAYS** add the URI of the post author to the `canLike.always`, `canReply.always`, and `canAnnounce.always` arrays, unless they are already covered by the ActivityStreams magic public URI.
|
|
||||||
|
|
||||||
Likewise, when enforcing received interaction policies, GoToSocial will **ALWAYS** behave as though the URI of the post author is present in these `always` arrays, even if it wasn't.
|
|
||||||
|
|
||||||
### Defaults
|
|
||||||
|
|
||||||
When the `interactionPolicy` property is not present at all on a post, GoToSocial assumes a default `interactionPolicy` for that post appropriate to the visibility level of the post, and the post author.
|
|
||||||
|
|
||||||
For a **public** or **unlocked** post by `@someone@example.org`, the default `interactionPolicy` is:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
[...],
|
|
||||||
"interactionPolicy": {
|
|
||||||
"canLike": {
|
|
||||||
"always": [
|
|
||||||
"https://www.w3.org/ns/activitystreams#Public"
|
|
||||||
],
|
|
||||||
"approvalRequired": []
|
|
||||||
},
|
|
||||||
"canReply": {
|
|
||||||
"always": [
|
|
||||||
"https://www.w3.org/ns/activitystreams#Public"
|
|
||||||
],
|
|
||||||
"approvalRequired": []
|
|
||||||
},
|
|
||||||
"canAnnounce": {
|
|
||||||
"always": [
|
|
||||||
"https://www.w3.org/ns/activitystreams#Public"
|
|
||||||
],
|
|
||||||
"approvalRequired": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[...]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
For a **followers-only** post by `@someone@example.org`, the assumed `interactionPolicy` is:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
[...],
|
|
||||||
"interactionPolicy": {
|
|
||||||
"canLike": {
|
|
||||||
"always": [
|
|
||||||
"https://example.org/users/someone",
|
|
||||||
"https://example.org/users/someone/followers",
|
|
||||||
[...URIs of any mentioned users...]
|
|
||||||
],
|
|
||||||
"approvalRequired": []
|
|
||||||
},
|
|
||||||
"canReply": {
|
|
||||||
"always": [
|
|
||||||
"https://example.org/users/someone",
|
|
||||||
"https://example.org/users/someone/followers",
|
|
||||||
[...URIs of any mentioned users...]
|
|
||||||
],
|
|
||||||
"approvalRequired": []
|
|
||||||
},
|
|
||||||
"canAnnounce": {
|
|
||||||
"always": [
|
|
||||||
"https://example.org/users/someone"
|
|
||||||
],
|
|
||||||
"approvalRequired": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[...]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
For a **direct** post by `@someone@example.org`, the assumed `interactionPolicy` is:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
[...],
|
|
||||||
"interactionPolicy": {
|
|
||||||
"canLike": {
|
|
||||||
"always": [
|
|
||||||
"https://example.org/users/someone",
|
|
||||||
[...URIs of any mentioned users...]
|
|
||||||
],
|
|
||||||
"approvalRequired": []
|
|
||||||
},
|
|
||||||
"canReply": {
|
|
||||||
"always": [
|
|
||||||
"https://example.org/users/someone",
|
|
||||||
[...URIs of any mentioned users...]
|
|
||||||
],
|
|
||||||
"approvalRequired": []
|
|
||||||
},
|
|
||||||
"canAnnounce": {
|
|
||||||
"always": [
|
|
||||||
"https://example.org/users/someone"
|
|
||||||
],
|
|
||||||
"approvalRequired": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[...]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Example 1 - Limiting scope of a conversation
|
|
||||||
|
|
||||||
In this example, the user `@the_mighty_zork` wants to begin a conversation with the users `@booblover6969` and `@hodor`.
|
|
||||||
|
|
||||||
To avoid the discussion being derailed by others, they want replies to their post by users other than the three participants to be permitted only if they're approved by `@the_mighty_zork`.
|
|
||||||
|
|
||||||
Furthermore, they want to limit the boosting / `Announce`ing of their post to only their own followers, and to the three conversation participants.
|
|
||||||
|
|
||||||
However, anyone should be able to `Like` the post by `@the_mighty_zork`.
|
|
||||||
|
|
||||||
This can be achieved with the following `interactionPolicy`, which is attached to a post with visibility level public:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
[...],
|
|
||||||
"interactionPolicy": {
|
|
||||||
"canLike": {
|
|
||||||
"always": [
|
|
||||||
"https://www.w3.org/ns/activitystreams#Public"
|
|
||||||
],
|
|
||||||
"approvalRequired": []
|
|
||||||
},
|
|
||||||
"canReply": {
|
|
||||||
"always": [
|
|
||||||
"https://example.org/users/the_mighty_zork",
|
|
||||||
"https://example.org/users/booblover6969",
|
|
||||||
"https://example.org/users/hodor"
|
|
||||||
],
|
|
||||||
"approvalRequired": [
|
|
||||||
"https://www.w3.org/ns/activitystreams#Public"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"canAnnounce": {
|
|
||||||
"always": [
|
|
||||||
"https://example.org/users/the_mighty_zork",
|
|
||||||
"https://example.org/users/the_mighty_zork/followers",
|
|
||||||
"https://example.org/users/booblover6969",
|
|
||||||
"https://example.org/users/hodor"
|
|
||||||
],
|
|
||||||
"approvalRequired": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[...]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Example 2 - Long solo thread
|
|
||||||
|
|
||||||
In this example, the user `@the_mighty_zork` wants to write a long solo thread.
|
|
||||||
|
|
||||||
They don't mind if people boost and like posts in the thread, but they don't want to get any replies because they don't have the energy to moderate the discussion; they just want to vent by throwing their thoughts out there.
|
|
||||||
|
|
||||||
This can be achieved by setting the following `interactionPolicy` on every post in the thread:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
[...],
|
|
||||||
"interactionPolicy": {
|
|
||||||
"canLike": {
|
|
||||||
"always": [
|
|
||||||
"https://www.w3.org/ns/activitystreams#Public"
|
|
||||||
],
|
|
||||||
"approvalRequired": []
|
|
||||||
},
|
|
||||||
"canReply": {
|
|
||||||
"always": [
|
|
||||||
"https://example.org/users/the_mighty_zork"
|
|
||||||
],
|
|
||||||
"approvalRequired": []
|
|
||||||
},
|
|
||||||
"canAnnounce": {
|
|
||||||
"always": [
|
|
||||||
"https://www.w3.org/ns/activitystreams#Public"
|
|
||||||
],
|
|
||||||
"approvalRequired": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[...]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Here, anyone is allowed to like or boost, but nobody is permitted to reply (except `@the_mighty_zork` themself).
|
|
||||||
|
|
||||||
### Example 3 - Completely open
|
|
||||||
|
|
||||||
In this example, `@the_mighty_zork` wants to write a completely open post that can be replied to, boosted, or liked by anyone who can see it (ie., the default behavior for unlocked and public posts):
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
[...],
|
|
||||||
"interactionPolicy": {
|
|
||||||
"canLike": {
|
|
||||||
"always": [
|
|
||||||
"https://www.w3.org/ns/activitystreams#Public"
|
|
||||||
],
|
|
||||||
"approvalRequired": []
|
|
||||||
},
|
|
||||||
"canReply": {
|
|
||||||
"always": [
|
|
||||||
"https://www.w3.org/ns/activitystreams#Public"
|
|
||||||
],
|
|
||||||
"approvalRequired": []
|
|
||||||
},
|
|
||||||
"canAnnounce": {
|
|
||||||
"always": [
|
|
||||||
"https://www.w3.org/ns/activitystreams#Public"
|
|
||||||
],
|
|
||||||
"approvalRequired": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[...]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Requesting, Obtaining, and Validating Approval
|
|
||||||
|
|
||||||
When a user's URI is in the `approvalRequired` array for a type of interaction, and that user wishes to obtain approval to distribute an interaction, they should do the following:
|
|
||||||
|
|
||||||
1. Compose the interaction `Activity` (ie., `Like`, `Create` (reply), or `Announce`), as normal.
|
|
||||||
2. Address the `Activity` `to` and `cc` the expected recipients for that `Activity`, as normal.
|
|
||||||
3. `POST` the `Activity` only to the `Inbox` (or `sharedInbox`) of the author of the post being interacted with.
|
|
||||||
4. **DO NOT DISTRIBUTE THE ACTIVITY FURTHER THAN THIS AT THIS POINT**.
|
|
||||||
|
|
||||||
At this point, the interaction can be considered as pending approval, and should not be shown in the replies or likes collections, etc., of the post interacted with.
|
|
||||||
|
|
||||||
It may be shown to the user who sent the interaction as a sort of "interaction pending" modal, but ideally it should not be shown to other users who share an instance with that user.
|
|
||||||
|
|
||||||
From here, one of three things may happen:
|
|
||||||
|
|
||||||
#### Rejection
|
|
||||||
|
|
||||||
In this scenario, the author of the post being interacted with sends back a `Reject` `Activity` with the URI/ID of the interaction `Activity` as the `Object` property.
|
|
||||||
|
|
||||||
For example, the following json object `Reject`s the attempt of `@someone@somewhere.else.example.org` to reply to a post by `@post_author@example.org`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"@context": "https://www.w3.org/ns/activitystreams",
|
|
||||||
"actor": "https://example.org/users/post_author",
|
|
||||||
"to": "https://somewhere.else.example.org/users/someone",
|
|
||||||
"id": "https://example.org/users/post_author/activities/reject/01J0K2YXP9QCT5BE1JWQSAM3B6",
|
|
||||||
"object": "https://somewhere.else.example.org/users/someone/statuses/01J17XY2VXGMNNPH1XR7BG2524",
|
|
||||||
"type": "Reject"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
If this happens, `@someone@somewhere.else.example.org` (and their instance) should consider the interaction as having been rejected. The instance should delete the activity from its internal storage (ie., database), or otherwise indicate that it's been rejected, and it should not distribute the `Activity` further, or retry the interaction.
|
|
||||||
|
|
||||||
#### Nothing
|
|
||||||
|
|
||||||
In this scenario, the author of the post being interacted with never sends back a `Reject` or an `Accept` `Activity`. In such a case, the interaction is considered "pending" in perpetuity. Instances may wish to implement some kind of cleanup feature, where sent and pending interactions that reach a certain age should be considered expired, and `Rejected` and then removed in the manner gestured towards above.
|
|
||||||
|
|
||||||
#### Acceptance
|
|
||||||
|
|
||||||
In this scenario, the author of the post being interacted with sends back an `Accept` `Activity` with the URI/ID of the interaction `Activity` as the `Object` property.
|
|
||||||
|
|
||||||
For example, the following json object `Accept`s the attempt of `@someone@somewhere.else.example.org` to reply to a post by `@post_author@example.org`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"@context": "https://www.w3.org/ns/activitystreams",
|
|
||||||
"actor": "https://example.org/users/post_author",
|
|
||||||
"cc": [
|
|
||||||
"https://www.w3.org/ns/activitystreams#Public",
|
|
||||||
"https://example.org/users/post_author/followers"
|
|
||||||
],
|
|
||||||
"to": "https://somewhere.else.example.org/users/someone",
|
|
||||||
"id": "https://example.org/users/post_author/activities/reject/01J0K2YXP9QCT5BE1JWQSAM3B6",
|
|
||||||
"object": "https://somewhere.else.example.org/users/someone/statuses/01J17XY2VXGMNNPH1XR7BG2524",
|
|
||||||
"type": "Accept"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
If this happens, `@someone@somewhere.else.example.org` (and their instance) should consider the interaction as having been approved / accepted. The instance can then feel free to distribute the interaction `Activity` to all of the recipients targed by `to`, `cc`, etc, with the additional property `approvedBy` ([see below](#approvedby)).
|
|
||||||
|
|
||||||
!!! Note
|
|
||||||
In the above example, actor `https://example.org/users/post_author` addresses the `Accept` activity not just to the interacting actor `https://somewhere.else.example.org/users/someone`, but to their followers collection as well (and, implicitly, to the public). This allows followers of `https://example.org/users/post_author` on other servers to also mark the interaction as accepted, and to show the interaction alongside the interacted-with post.
|
|
||||||
|
|
||||||
### Validating presence in a Followers / Following collection
|
|
||||||
|
|
||||||
If an `Actor` interacting with an `Object` (via `Like`, `inReplyTo`, or `Announce`) is permitted to do that interaction based on their presence in a `Followers` or `Following` collection in the `always` field of an interaction policy, then their server should *still* wait for an `Accept` to be received from the server of the target account, before distributing the interaction more widely with the `approvedBy` property set to the URI of the `Accept`.
|
|
||||||
|
|
||||||
This is to prevent scenarios where third servers have to somehow verify the presence of the interacting `Actor` in the `Followers` or `Following` collection of the `Actor` being interacted with. It is simpler to allow the target server to do that verification, and to trust that their `Accept` implicitly agrees that the interacting `Actor` is present in the relevant collection.
|
|
||||||
|
|
||||||
Likewise, when receiving an interaction from an `Actor` whose permission to interact matches with one of the `Following` or `Followers` collections in the `always` property, the server of the interacted-with `Actor` should ensure that they *always* send out an `Accept` as soon as possible, so that the interacting `Actor` server can send out the `Activity` with the proper proof of acceptance.
|
|
||||||
|
|
||||||
This process should bypass the normal "pending approval" stage whereby the server of the `Actor` being interacted with notifies them of the pending interaction, and waits for them to accept or reject, since there is no point notifying an `Actor` of a pending approval that they have already explicitly agreed to. In the GoToSocial codebase in particular, this is called "preapproval".
|
|
||||||
|
|
||||||
### `approvedBy`
|
|
||||||
|
|
||||||
`approvedBy` is an additional property added to the `Like`, and `Announce` activities, and any `Object`s considered to be "posts" (`Note`, `Article`, etc).
|
|
||||||
|
|
||||||
The presence of `approvedBy` signals that the author of the post targeted by the `Activity` or replied-to by the `Object` has approved/accepted the interaction, and it can now be distributed to its intended audience.
|
|
||||||
|
|
||||||
The value of `approvedBy` should be the URI of the `Accept` `Activity` created by the author of the post being interacted with.
|
|
||||||
|
|
||||||
For example, the following `Announce` `Activity` indicates, by the presence of `approvedBy`, that it has been `Accept`ed by `@post_author@example.org`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"actor": "https://somewhere.else.example.org/users/someone",
|
|
||||||
"to": [
|
|
||||||
"https://somewhere.else.example.org/users/someone/followers"
|
|
||||||
],
|
|
||||||
"cc": [
|
|
||||||
"https://example.org/users/post_author"
|
|
||||||
],
|
|
||||||
"id": "https://somewhere.else.example.org/users/someone/activities/announce/01J0K2YXP9QCT5BE1JWQSAM3B6",
|
|
||||||
"object": "https://example.org/users/post_author/statuses/01J17ZZFK6W82K9MJ9SYQ33Y3D",
|
|
||||||
"approvedBy": "https://example.org/users/post_author/activities/accept/01J18043HGECBDZQPT09CP6F2X",
|
|
||||||
"type": "Announce"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
When receiving an `Activity` with an `approvedBy` value attached to it, remote instances should dereference the URI value of the field to get the `Accept` `Activity`.
|
|
||||||
|
|
||||||
They should then validate that the `Accept` `Activity` has an `object` value equal to the `id` of the interaction `Activity` or `Object`, and an `actor` value equal to the author of the post being interacted with.
|
|
||||||
|
|
||||||
Moreover, they should ensure that the URL host/domain of the dereferenced `Accept` is equal to the URL host/domain of the author of the post being interacted with.
|
|
||||||
|
|
||||||
If the `Accept` cannot be dereferenced, or does not pass validity checks, the interaction should be considered invalid and dropped.
|
|
||||||
|
|
||||||
As a consequence of this validadtion mechanism, instances should make sure that they serve a valid ActivityPub Object in response to dereferences of `Accept` URIs that pertain to an `interactionPolicy`. If they do not, they inadvertently risk restricting the ability of remote instances to distribute their posts.
|
|
||||||
|
|
||||||
### Subsequent Replies / Scope Widening
|
|
||||||
|
|
||||||
Each subsequent reply in a conversation will have its own interaction policy, chosen by the user who created the reply. In other words, the entire *conversation* or *thread* is not controlled by one `interactionPolicy`, but the policy can differ for each subsequent post in a thread, as set by the post author.
|
|
||||||
|
|
||||||
Unfortunately, this means that even with `interactionPolicy` in place, the scope of a thread can inadvertently widen beyond the intention of the author of the first post in the thread.
|
|
||||||
|
|
||||||
For instance, in [example 1](#example-1---limiting-scope-of-a-conversation) above, `@the_mighty_zork` specifies in the first post a `canReply.always` value of
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
"https://example.org/users/the_mighty_zork",
|
|
||||||
"https://example.org/users/booblover6969",
|
|
||||||
"https://example.org/users/hodor"
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
In a subsequent reply, either accidentally or on purpose `@booblover6969` sets the `canReply.always` value to:
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
"https://www.w3.org/ns/activitystreams#Public"
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
This widens the scope of the conversation, as now anyone can reply to `@booblover6969`'s post, and possibly also tag `@the_mighty_zork` in that reply.
|
|
||||||
|
|
||||||
To avoid this issue, it is recommended that remote instances prevent users from being able to widen scope (exact mechanism of doing this TBD).
|
|
||||||
|
|
||||||
It is also a good idea for instances to consider any interaction with a post- or status-like `Object` that is itself currently pending approval, as also pending approval.
|
|
||||||
|
|
||||||
In other words, instances should mark all children interactions below a pending-approval parent as also pending approval, no matter what the interaction policy on the parent would ordinarily allow.
|
|
||||||
|
|
||||||
This avoids situations where someone could reply to a post, then, even if their reply is pending approval, they could reply *to their own reply* and have that marked as permitted (since as author, they would normally have [implicit permission to reply](#implicit-assumptions)).
|
|
||||||
|
|
||||||
## Polls
|
## Polls
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ For media storage, including [storage of remote media files in the cache](../adm
|
||||||
|
|
||||||
### Single-board Computers
|
### Single-board Computers
|
||||||
|
|
||||||
GoToSocial's light system requirements means that it runs pretty well on decently-specced single-board computers. If running on a single-board computer, you should ensure that GoToSocial is using a USB drive (preferably an SSD) to store its database files and media, not SD card storage, since the latter tends to be too slow to run a database on.
|
GoToSocial's light system requirements means that it runs pretty well on decently-specced single-board computers. If running on a single-board computer, you should ensure that GoToSocial is using a USB drive (preferably an SSD) to store its database files and media, not SD card storage, since the latter tends to be too slow to run a database on and can easily get corrupted with write-heavy workloads.
|
||||||
|
|
||||||
### VPS
|
### VPS
|
||||||
|
|
||||||
|
|
@ -68,7 +68,7 @@ If you decide to use a VPS instead, you can spin yourself up something cheap wit
|
||||||
!!! warning "Cloud storage volumes"
|
!!! warning "Cloud storage volumes"
|
||||||
Not all cloud VPS storage offerings are equal, and just because something claims to be backed by an SSD doesn't mean that it will necessarily be suitable to run a GoToSocial instance.
|
Not all cloud VPS storage offerings are equal, and just because something claims to be backed by an SSD doesn't mean that it will necessarily be suitable to run a GoToSocial instance.
|
||||||
|
|
||||||
The [performance of Hetzner Cloud Volumes](https://github.com/superseriousbusiness/gotosocial/issues/2471#issuecomment-1891098323) is not guaranteed and seems to have very volatile latency. This will result in your GoToSocial instance performing poorly.
|
The [performance of Hetzner Cloud Volumes](https://codeberg.org/superseriousbusiness/gotosocial/issues/2471#issuecomment-1891098323) is not guaranteed and seems to have very volatile latency. This will result in your GoToSocial instance performing poorly.
|
||||||
|
|
||||||
!!! danger "Oracle Free Tier"
|
!!! danger "Oracle Free Tier"
|
||||||
[Oracle Cloud Free Tier](https://www.oracle.com/cloud/free/) servers are not suitable for a GoToSocial deployment if you intend to federate with more than a handful of other instances and users.
|
[Oracle Cloud Free Tier](https://www.oracle.com/cloud/free/) servers are not suitable for a GoToSocial deployment if you intend to federate with more than a handful of other instances and users.
|
||||||
|
|
@ -149,13 +149,13 @@ If you can't leave `443` and `80` open on the machine, don't worry! You can conf
|
||||||
|
|
||||||
## Clustering / multi-node deployments
|
## Clustering / multi-node deployments
|
||||||
|
|
||||||
GoToSocial does not support [clustering or any form of multi-node deployment](https://github.com/superseriousbusiness/gotosocial/issues/1749).
|
GoToSocial does not support [clustering or any form of multi-node deployment](https://codeberg.org/superseriousbusiness/gotosocial/issues/1749).
|
||||||
|
|
||||||
Though multiple GtS instances can use the same Postgres database and either shared local storage or the same object bucket, GtS relies on a lot of internal caching to keep things fast. There is no mechanism for synchronising these caches between instances. Without it, you'll get all kinds of odd and inconsistent behaviour. Don't do this!
|
Though multiple GtS instances can use the same Postgres database and either shared local storage or the same object bucket, GtS relies on a lot of internal caching to keep things fast. There is no mechanism for synchronising these caches between instances. Without it, you'll get all kinds of odd and inconsistent behaviour. Don't do this!
|
||||||
|
|
||||||
## Tuning
|
## Tuning
|
||||||
|
|
||||||
Aside from the many instance tuning options present in the [example config file](https://github.com/superseriousbusiness/gotosocial/blob/main/example/config.yaml) you can do additional tuning on the machine your GoToSocial instance is running on.
|
Aside from the many instance tuning options present in the [example config file](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/example/config.yaml) you can do additional tuning on the machine your GoToSocial instance is running on.
|
||||||
|
|
||||||
### Swap
|
### Swap
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,10 @@ cd ~/gotosocial
|
||||||
|
|
||||||
## Get the latest docker-compose.yaml
|
## 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:
|
Use `wget` to download the latest [docker-compose.yaml](https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/example/docker-compose/docker-compose.yaml) example, which we'll customize for our needs:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
wget https://raw.githubusercontent.com/superseriousbusiness/gotosocial/main/example/docker-compose/docker-compose.yaml
|
wget https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/example/docker-compose/docker-compose.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
## Edit the docker-compose.yaml
|
## Edit the docker-compose.yaml
|
||||||
|
|
@ -42,10 +42,10 @@ If desired, update the GoToSocial Docker image tag to the version of GtS you wan
|
||||||
|
|
||||||
* `latest`: the default. This points to the latest stable release of GoToSocial.
|
* `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.
|
* `snapshot`: points to whatever code is currently on the main branch. Not guaranteed to be stable, and may often be broken. Use with caution.
|
||||||
* `vX.Y.Z`: release tag. This points to a specific, stable, release of GoToSocial.
|
* `X.Y.Z`: release tag. This points to a specific, stable, release of GoToSocial.
|
||||||
|
|
||||||
!!! tip
|
!!! tip
|
||||||
Both the `latest` and `snapshot` tags are moving tags, whereas the `vX.Y.Z` tags are immutable. The result of pulling a moving tag might change from day to day. `latest` on one system might not be the same `latest` on a different system. It's recommended to use the `vX.Y.Z` tags instead so you always know exactly which version of GoToSocial you're running. The list of releases can be found [right here](https://github.com/superseriousbusiness/gotosocial/releases), with the newest release at the top.
|
Both the `latest` and `snapshot` tags are moving tags, whereas the `X.Y.Z` tags are immutable. The result of pulling a moving tag might change from day to day. `latest` on one system might not be the same `latest` on a different system. It's recommended to use the `X.Y.Z` tags instead so you always know exactly which version of GoToSocial you're running. The list of releases can be found [right here](https://codeberg.org/superseriousbusiness/gotosocial/releases), with the newest release at the top.
|
||||||
|
|
||||||
### Host
|
### Host
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,14 +34,14 @@ cd /gotosocial
|
||||||
Now, download the latest GoToSocial release archive corresponding to the operating system and architecture you're running on.
|
Now, download the latest GoToSocial release archive corresponding to the operating system and architecture you're running on.
|
||||||
|
|
||||||
!!! tip
|
!!! tip
|
||||||
You can find the list of releases [right here](https://github.com/superseriousbusiness/gotosocial/releases), arranged with the newest release at the top.
|
You can find the list of releases [right here](https://codeberg.org/superseriousbusiness/gotosocial/releases), arranged with the newest release at the top.
|
||||||
|
|
||||||
For example, to download a version for running on 64-bit Linux:
|
For example, to download a version for running on 64-bit Linux:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
GTS_VERSION=X.Y.Z # replace this
|
GTS_VERSION=X.Y.Z # replace this
|
||||||
GTS_TARGET=linux_amd64
|
GTS_TARGET=linux_amd64
|
||||||
wget https://github.com/superseriousbusiness/gotosocial/releases/download/v${GTS_VERSION}/gotosocial_${GTS_VERSION}_${GTS_TARGET}.tar.gz
|
wget https://codeberg.org/superseriousbusiness/gotosocial/releases/download/v${GTS_VERSION}/gotosocial_${GTS_VERSION}_${GTS_TARGET}.tar.gz
|
||||||
```
|
```
|
||||||
|
|
||||||
Then extract it:
|
Then extract it:
|
||||||
|
|
@ -63,8 +63,6 @@ You'll probably need to change the following settings:
|
||||||
|
|
||||||
- Set `host` to whatever hostname you're going to be running the server on (eg., `example.org`).
|
- Set `host` to whatever hostname you're going to be running the server on (eg., `example.org`).
|
||||||
- Set `port` to `443`.
|
- Set `port` to `443`.
|
||||||
- Set `db-type` to `sqlite`.
|
|
||||||
- Set `db-address` to `sqlite.db`.
|
|
||||||
- Set `storage-local-base-path` to the storage directory you created above (eg., `/gotosocial/storage`).
|
- Set `storage-local-base-path` to the storage directory you created above (eg., `/gotosocial/storage`).
|
||||||
- Set `letsencrypt-enabled` to `true`.
|
- Set `letsencrypt-enabled` to `true`.
|
||||||
- Set `letsencrypt-cert-dir` to the certificate storage directory you created above (eg., `/gotosocial/storage/certs`).
|
- Set `letsencrypt-cert-dir` to the certificate storage directory you created above (eg., `/gotosocial/storage/certs`).
|
||||||
|
|
@ -123,7 +121,7 @@ Then make them the owner of your GoToSocial installation since they will need to
|
||||||
sudo chown -R gotosocial:gotosocial /gotosocial
|
sudo chown -R gotosocial:gotosocial /gotosocial
|
||||||
```
|
```
|
||||||
|
|
||||||
You can find a `gotosocial.service` file in the `example` folder on [github](https://raw.githubusercontent.com/superseriousbusiness/gotosocial/main/example/gotosocial.service) or your installation.
|
You can find a `gotosocial.service` file in the `example` folder on [our repository](https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/example/gotosocial.service) or your installation.
|
||||||
|
|
||||||
Copy it to `/etc/systemd/system/gotosocial.service`:
|
Copy it to `/etc/systemd/system/gotosocial.service`:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -178,7 +178,7 @@ hooks:
|
||||||
|
|
||||||
对于 PostgreSQL,你应该使用 `postgresql_databases`。
|
对于 PostgreSQL,你应该使用 `postgresql_databases`。
|
||||||
|
|
||||||
`patterns_from` 中提到的文件可以通过转换 GoToSocial CLI 媒体命令 [`list-attachments`](cli.md#gotosocial-admin-media-list-attachments) 和 [`list-emojis`](cli.md#gotosocial-admin-media-list-emojis) 的输出来创建。要生成正确的模式,您可以使用 [`media-to-borg-patterns.py`](https://github.com/superseriousbusiness/gotosocial/tree/main/example/borgmatic/media-to-borg-patterns.py) 脚本。有关 Borg 模式如何工作的详情,参见 [他们的文档](https://man.archlinux.org/man/borg-patterns.1)。
|
`patterns_from` 中提到的文件可以通过转换 GoToSocial CLI 媒体命令 [`list-attachments`](cli.md#gotosocial-admin-media-list-attachments) 和 [`list-emojis`](cli.md#gotosocial-admin-media-list-emojis) 的输出来创建。要生成正确的模式,您可以使用 [`media-to-borg-patterns.py`](https://codeberg.org/superseriousbusiness/gotosocial/tree/main/example/borgmatic/media-to-borg-patterns.py) 脚本。有关 Borg 模式如何工作的详情,参见 [他们的文档](https://man.archlinux.org/man/borg-patterns.1)。
|
||||||
|
|
||||||
您需要将该文件放在您的 GoToSocial 实例上,并确保该文件是可执行的。它需要 Python 3,安装 Borg 和 Borgmatic 后您应该已经具备。它仅依赖于 Python 标准库。
|
您需要将该文件放在您的 GoToSocial 实例上,并确保该文件是可执行的。它需要 Python 3,安装 Borg 和 Borgmatic 后您应该已经具备。它仅依赖于 Python 标准库。
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,11 @@ GoToSocial 编译为二进制可执行文件。
|
||||||
以下是 `gotosocial --help` 的完整输出,不包括全局配置选项的大列表。
|
以下是 `gotosocial --help` 的完整输出,不包括全局配置选项的大列表。
|
||||||
|
|
||||||
```text
|
```text
|
||||||
GoToSocial - 一个联邦制社交媒体服务器
|
GoToSocial - 一个联合式社交媒体服务器
|
||||||
|
|
||||||
帮助文档参见:https://docs.gotosocial.org。
|
帮助文档参见:https://docs.gotosocial.org/zh-cn/。
|
||||||
|
|
||||||
代码仓库:https://github.com/superseriousbusiness/gotosocial
|
代码仓库:https://codeberg.org/superseriousbusiness/gotosocial
|
||||||
|
|
||||||
用法:
|
用法:
|
||||||
gotosocial [command]
|
gotosocial [command]
|
||||||
|
|
@ -57,6 +57,9 @@ GoToSocial - 一个联邦制社交媒体服务器
|
||||||
|
|
||||||
此命令可用于在你的实例上创建新账户。
|
此命令可用于在你的实例上创建新账户。
|
||||||
|
|
||||||
|
!!! Warning "警告"
|
||||||
|
执行此命令前,你必须至少运行过一次服务端,以初始化数据库中的必要条目,然后才能运行此命令。
|
||||||
|
|
||||||
`gotosocial admin account create --help`:
|
`gotosocial admin account create --help`:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@
|
||||||
|
|
||||||
例如,实例 `instance-a.example.org`、`instance-b.example.org` 和 `instance-c.example.org` 决定他们只想彼此联合。
|
例如,实例 `instance-a.example.org`、`instance-b.example.org` 和 `instance-c.example.org` 决定他们只想彼此联合。
|
||||||
|
|
||||||
他们可以使用像 GitHub 这样的版本管理平台托管一个纯文本格式的允许列表,比如在 `https://raw.githubusercontent.com/our-cluster/allowlist/refs/heads/main/allows.txt`。
|
他们可以使用像 Codeberg 这样的版本管理平台托管一个纯文本格式的允许列表,比如在 `https://codeberg.org/our-cluster/allowlist/raw/branch/main/allows.txt`。
|
||||||
|
|
||||||
纯文本格式的允许列表内容如下:
|
纯文本格式的允许列表内容如下:
|
||||||
|
|
||||||
|
|
@ -47,7 +47,7 @@ instance-b.example.org
|
||||||
instance-c.example.org
|
instance-c.example.org
|
||||||
```
|
```
|
||||||
|
|
||||||
每个实例管理员都将他们的联合模式设置为`白名单`,并创建一个类型为“允许”,订阅地址为 `https://raw.githubusercontent.com/our-cluster/allowlist/refs/heads/main/allows.txt` 的订阅,这会为他们自己的域名以及集群中的其他域名创建域名允许条目。
|
每个实例管理员都将他们的联合模式设置为`allowlist`,并创建一个类型为“允许”,订阅地址为 `https://codeberg.org/our-cluster/allowlist/raw/branch/main/allows.txt` 的订阅,这会为他们自己的域名以及集群中的其他域名创建域名允许条目。
|
||||||
|
|
||||||
在某个时候,来自 `instance-d.example.org` 的某人(在站外)申请被添加到集群中。现有的管理员同意,并更新他们的纯文本格式允许列表为:
|
在某个时候,来自 `instance-d.example.org` 的某人(在站外)申请被添加到集群中。现有的管理员同意,并更新他们的纯文本格式允许列表为:
|
||||||
|
|
||||||
|
|
@ -66,7 +66,7 @@ instance-d.example.org
|
||||||
|
|
||||||
例如,实例 `instance-e.example.org`、`instance-f.example.org` 和 `instance-g.example.org` 的管理员认定:他们厌倦了通过与坏人玩打地鼠游戏来重复工作。为了让生活更轻松,他们决定合作开发一个共享的阻止列表。
|
例如,实例 `instance-e.example.org`、`instance-f.example.org` 和 `instance-g.example.org` 的管理员认定:他们厌倦了通过与坏人玩打地鼠游戏来重复工作。为了让生活更轻松,他们决定合作开发一个共享的阻止列表。
|
||||||
|
|
||||||
他们使用像 GitHub 这样的版本管理平台在类似 `https://raw.githubusercontent.com/baddies/blocklist/refs/heads/main/blocks.csv` 的地方托管一个阻止列表。
|
他们使用像 Codeberg 这样的版本管理平台在类似 `https://codeberg.org/our-cluster/allowlist/raw/branch/main/blocks.csv` 的地方托管一个阻止列表。
|
||||||
|
|
||||||
当有人发现另一个他们不喜欢的实例时,他们可以通过合并请求或类似方法添加这个有问题的实例到域名列表中。
|
当有人发现另一个他们不喜欢的实例时,他们可以通过合并请求或类似方法添加这个有问题的实例到域名列表中。
|
||||||
|
|
||||||
|
|
@ -113,6 +113,27 @@ nothanks.com,suspend,false,false,,false
|
||||||
|
|
||||||
JSON列表使用内容类型 `application/json`。
|
JSON列表使用内容类型 `application/json`。
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"domain": "bumfaces.net",
|
||||||
|
"suspended_at": "2020-05-13T13:29:12.000Z",
|
||||||
|
"comment": "这个实例上有坏蛋"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "peepee.poopoo",
|
||||||
|
"suspended_at": "2020-05-13T13:29:12.000Z",
|
||||||
|
"comment": "骚扰"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "nothanks.com",
|
||||||
|
"suspended_at": "2020-05-13T13:29:12.000Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
可以使用 `"comment"` 字段替代 `"public_comment"`:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -31,13 +31,18 @@ GoToSocial 当前提供“黑名单”和“白名单”联合模式,可以通
|
||||||
|
|
||||||
## 结合屏蔽与允许
|
## 结合屏蔽与允许
|
||||||
|
|
||||||
可以同时屏蔽和允许同一个域,结合这两者的效果取决于你的实例当前使用的联合模式。
|
!!! danger "危险"
|
||||||
|
结合屏蔽和允许是一项棘手的工作!
|
||||||
|
|
||||||
|
在导入白名单和黑名单时,你应该始终手动审核列表,以确保不会无意中屏蔽你不想屏蔽的实例,因为这可能带来 **非常令人困扰的副作用** ,例如关注/粉丝关系的移除、贴文的删除等。
|
||||||
|
|
||||||
|
有疑问时,请始终首先显式添加白名单条目作为保险策略!
|
||||||
|
|
||||||

|
同时屏蔽和允许同一个域名是合法的,而这两个操作结合之后的效果取决于你的实例目前正在使用哪种联合模式,具体如下所述,你可以在下面找到流程图。
|
||||||
|
|
||||||
### 在黑名单模式下
|
### 在黑名单模式下
|
||||||
|
|
||||||
如图所示,在黑名单模式下(图的左侧),显式添加允许条目可以用来覆盖域名屏蔽。
|
如下图所示,在黑名单模式下(图的左侧),显式添加允许条目可以用来覆盖域名屏蔽。
|
||||||
|
|
||||||
这在你从其他人处导入黑名单,但导入的黑名单中包含了一些你实际上不想屏蔽的实例时很有用。为了避免屏蔽这些实例,你可以先为这些实例显式创建允许条目。然后,当你导入黑名单时,显式允许的域将不会被屏蔽,并且创建屏蔽所导致的副作用(删除贴文、媒体、关系等)将不会被处理。
|
这在你从其他人处导入黑名单,但导入的黑名单中包含了一些你实际上不想屏蔽的实例时很有用。为了避免屏蔽这些实例,你可以先为这些实例显式创建允许条目。然后,当你导入黑名单时,显式允许的域将不会被屏蔽,并且创建屏蔽所导致的副作用(删除贴文、媒体、关系等)将不会被处理。
|
||||||
|
|
||||||
|
|
@ -47,16 +52,11 @@ GoToSocial 当前提供“黑名单”和“白名单”联合模式,可以通
|
||||||
|
|
||||||
### 在白名单模式下
|
### 在白名单模式下
|
||||||
|
|
||||||
如图所示,在白名单模式下(图的右侧),显式域名屏蔽条目会优先于显式域名允许条目。在运行白名单模式时,必须满足以下两个条件才能允许一个实例通过:
|
如下图所示,在白名单模式下(图的右侧),显式域名屏蔽条目会优先于显式域名允许条目。在运行白名单模式时,必须满足以下两个条件才能允许一个实例通过:
|
||||||
|
|
||||||
1. 实例没有存在对应的显式域名屏蔽。
|
1. 实例没有存在对应的显式域名屏蔽。
|
||||||
2. 实例存在对应的显式域名允许。
|
2. 实例存在对应的显式域名允许。
|
||||||
|
|
||||||
如果上述任何条件不满足,请求将被拒绝。
|
如果上述任何条件不满足,请求将被拒绝。
|
||||||
|
|
||||||
!!! danger "危险"
|

|
||||||
结合屏蔽和允许是一项棘手的工作!
|
|
||||||
|
|
||||||
在导入允许和黑名单时,你应该始终手动审核列表,以确保不会无意中屏蔽你不想屏蔽的实例,因为这可能会有**非常烦人的副作用**,例如移除关注/被关注、贴文等。
|
|
||||||
|
|
||||||
有疑问时,请始终首先添加显式允许作为保险策略!
|
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,6 @@ GoToSocial 在主域名上提供一个 `robots.txt` 文件。该文件包含试
|
||||||
|
|
||||||
AI 爬虫来自一个[社区维护的仓库][airobots]。目前是手动保持同步的。如果你知道有任何遗漏的爬虫,请给他们提交一个 PR!
|
AI 爬虫来自一个[社区维护的仓库][airobots]。目前是手动保持同步的。如果你知道有任何遗漏的爬虫,请给他们提交一个 PR!
|
||||||
|
|
||||||
已知有许多 AI 爬虫即便明确匹配其 User-Agent,也会忽略 `robots.txt` 中的条目。这意味着 `robots.txt` 文件并不是确保 AI 爬虫不抓取你的内容的万无一失的方法。
|
众所周知,很多 AI 爬虫在 `robots.txt` 不允许其 User-Agent 的情况下,仍然会忽略对应规则并继续抓去内容。这意味着 `robots.txt` 文件并不是确保 AI 爬虫不抓取你的内容的万无一失的方法。除此以外,你可能还需要考虑通过[请求标头过滤](request_filtering_modes.md)来阻止对应 User-Agent。
|
||||||
|
|
||||||
如果你想完全屏蔽这些爬虫,需要在反向代理中根据 User-Agent 头进行屏蔽,直到 GoToSocial 能够根据 User-Agent 头过滤请求。
|
|
||||||
|
|
||||||
[airobots]: https://github.com/ai-robots-txt/ai.robots.txt/
|
[airobots]: https://github.com/ai-robots-txt/ai.robots.txt/
|
||||||
|
|
|
||||||
|
|
@ -46,13 +46,15 @@
|
||||||
|
|
||||||
## 注册限制
|
## 注册限制
|
||||||
|
|
||||||
为了避免注册积压过多使管理员和版主不堪重负,GoToSocial 将待处理注册积压限制为 20 个账户。一旦积压中有 20 个账户等待管理员或版主处理,新注册将不能通过表单提交。
|
为了避免积压的注册申请超出管理员和版主的处理能力,GoToSocial 默认将待处理注册积压限制为 20 个账号。一旦待处理队列中有 20 个账号等待管理员或版主处理,将不再通过表单接受新注册申请。
|
||||||
|
|
||||||
如果过去 24 小时内已批准 10 个或以上新账户注册,新的注册也将不能通过表单提交,以避免实例规模快速扩张超出管理能力。
|
默认情况下,如果在过去 24 小时内已批准的注册账号数达到或超过 10 个,新注册也将不再通过表单接受,以避免实例规模快速扩张超出管理能力。
|
||||||
|
|
||||||
在这两种情况下,申请人将看到一条错误信息,解释无法提交表单的原因,并邀请他们稍后再试。
|
在这两种情况下,申请者将看到一条错误信息,说明无法提交表单的原因,并邀请他们稍后再试。
|
||||||
|
|
||||||
为了防止垃圾账户,GoToSocial 的账户注册**始终**需要管理员手动批准,并且申请人**始终**需确认其电子邮件地址后才能登录和发布贴文。
|
每日注册的上限及待处理注册队列的大小可以通过 `accounts-registration-daily-limit` 和 `accounts-registration-backlog-limit` 这两个变量进行配置或完全禁用。更多信息请参见 [账户配置](../configuration/accounts.md)。
|
||||||
|
|
||||||
|
为了防止垃圾账户,GoToSocial 的账户注册**始终**需要管理员手动审批,并且申请人**始终**需确认其电子邮件地址后才能登录和发布贴文。
|
||||||
|
|
||||||
## 通过邀请注册
|
## 通过邀请注册
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,10 @@
|
||||||
|
|
||||||
使用 `nowasm` 构建的 GoToSocial 二进制文件将使用 [modernc 版本的 SQLite](https://pkg.go.dev/modernc.org/sqlite) 而不是 WASM 版本,并将在系统上使用 `ffmpeg` 和 `ffprobe` 二进制文件进行媒体处理。
|
使用 `nowasm` 构建的 GoToSocial 二进制文件将使用 [modernc 版本的 SQLite](https://pkg.go.dev/modernc.org/sqlite) 而不是 WASM 版本,并将在系统上使用 `ffmpeg` 和 `ffprobe` 二进制文件进行媒体处理。
|
||||||
|
|
||||||
|
!!! tip "提示"
|
||||||
|
要测试你的系统是否支持标准构建,可以使用此命令:
|
||||||
|
`if grep -qE '^flags.* (sse4|LSE)' /proc/cpuinfo; then echo "Your system is supporting GTS!"; else echo "Your system is not supporting GTS, you'll have to use the 'nowasm' builds :("; fi`
|
||||||
|
|
||||||
要使用 `nowasm` 标签构建 GoToSocial,可以像这样将标签传入我们的便利 `build.sh` 脚本:
|
要使用 `nowasm` 标签构建 GoToSocial,可以像这样将标签传入我们的便利 `build.sh` 脚本:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
||||||
|
|
@ -54,4 +54,4 @@ location /metrics {
|
||||||
|
|
||||||
[otel]: https://opentelemetry.io/
|
[otel]: https://opentelemetry.io/
|
||||||
[prom]: https://prometheus.io/docs/instrumenting/exposition_formats/
|
[prom]: https://prometheus.io/docs/instrumenting/exposition_formats/
|
||||||
[obs]: ../configuration/observability.md
|
[obs]: ../configuration/observability_and_metrics.md
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
我们提供了一个 GoToSocial 的 AppArmor 示例策略,你可以按以下步骤获取并安装:
|
我们提供了一个 GoToSocial 的 AppArmor 示例策略,你可以按以下步骤获取并安装:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ curl -LO 'https://github.com/superseriousbusiness/gotosocial/raw/main/example/apparmor/gotosocial'
|
$ curl -LO 'https://codeberg.org/superseriousbusiness/gotosocial/raw/main/example/apparmor/gotosocial'
|
||||||
$ sudo install -o root -g root gotosocial /etc/apparmor.d/gotosocial
|
$ sudo install -o root -g root gotosocial /etc/apparmor.d/gotosocial
|
||||||
$ sudo apparmor_parser -Kr /etc/apparmor.d/gotosocial
|
$ sudo apparmor_parser -Kr /etc/apparmor.d/gotosocial
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -32,4 +32,4 @@ SQLite 的运行模式假定数据库和使用它的进程或应用程序位于
|
||||||
* `db-sqlite-journal-mode`
|
* `db-sqlite-journal-mode`
|
||||||
* `db-sqlite-synchronous`
|
* `db-sqlite-synchronous`
|
||||||
|
|
||||||
我们不提供任何建议,因为这将根据你使用的解决方案而有所不同。请参阅 [此问题](https://github.com/superseriousbusiness/gotosocial/issues/3360#issuecomment-2380332027)以了解你可能设置的值。
|
我们不提供任何建议,因为这将根据你使用的解决方案而有所不同。请参阅 [此问题](https://codeberg.org/superseriousbusiness/gotosocial/issues/3360#issuecomment-2380332027)以了解你可能设置的值。
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,10 @@ tracing-insecure-transport: true
|
||||||
```
|
```
|
||||||
|
|
||||||
[otel]: https://opentelemetry.io/
|
[otel]: https://opentelemetry.io/
|
||||||
[obs]: ../configuration/observability.md
|
[obs]: ../configuration/observability_and_metrics.md
|
||||||
[tempo]: https://grafana.com/oss/tempo/
|
[tempo]: https://grafana.com/oss/tempo/
|
||||||
[grafana]: https://grafana.com/oss/grafana/
|
[grafana]: https://grafana.com/oss/grafana/
|
||||||
[ext]: https://github.com/superseriousbusiness/gotosocial/tree/main/example/tracing
|
[ext]: https://codeberg.org/superseriousbusiness/gotosocial/tree/main/example/tracing
|
||||||
|
|
||||||
## 查询和可视化追踪
|
## 查询和可视化追踪
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
使用客户端 API 需要进行身份验证。本页记录了如何获取身份验证令牌的通用流程,并提供了使用 `curl` 在命令行界面进行操作的示例。
|
使用客户端 API 需要进行身份验证。本页记录了如何获取身份验证令牌的通用流程,并提供了使用 `curl` 在命令行界面进行操作的示例。
|
||||||
|
|
||||||
|
!!! tip "提示"
|
||||||
|
如果你不想使用命令行,而是想通过设置面板获取 API 访问令牌,可以参考 [应用文档](https://docs.gotosocial.org/zh-cn/latest/user_guide/settings/#applications)。
|
||||||
|
|
||||||
## 创建新应用
|
## 创建新应用
|
||||||
|
|
||||||
我们需要注册一个新应用,以便请求 OAuth 令牌。这可以通过向 `/api/v1/apps` 端点发送 `POST` 请求来完成。注意将下面命令中的 `your_app_name` 替换为你想使用的应用名称:
|
我们需要注册一个新应用,以便请求 OAuth 令牌。这可以通过向 `/api/v1/apps` 端点发送 `POST` 请求来完成。注意将下面命令中的 `your_app_name` 替换为你想使用的应用名称:
|
||||||
|
|
@ -19,18 +22,15 @@ curl \
|
||||||
|
|
||||||
字符串 `urn:ietf:wg:oauth:2.0:oob` 表示一种称为带外身份验证的技术,这是一种用于多因素身份验证的技术,旨在减少恶意行为者干扰身份验证过程的途径。在此情况下,它允许我们查看并手动复制生成的令牌以便继续使用。
|
字符串 `urn:ietf:wg:oauth:2.0:oob` 表示一种称为带外身份验证的技术,这是一种用于多因素身份验证的技术,旨在减少恶意行为者干扰身份验证过程的途径。在此情况下,它允许我们查看并手动复制生成的令牌以便继续使用。
|
||||||
|
|
||||||
注意,`scopes` 可以是以下任意空格分隔的组合:
|
!!! tip "权限范围"
|
||||||
|
根据应用执行的工作对应用进行最低限度的授权是一个好习惯。例如,如果你的应用不会发布贴文,请使用 `scope=read` 或进一步仅授权子权限。
|
||||||
- `read`
|
|
||||||
- `write`
|
本着这种精神,上例使用了`read`,这意味着应用将仅限于执行`read`操作。
|
||||||
- `admin`
|
|
||||||
|
可用范围列表请参阅[Swagger 文档](https://docs.gotosocial.org/zh-cn/latest/api/swagger/).
|
||||||
|
|
||||||
!!! warning "警告"
|
!!! warning "警告"
|
||||||
GoToSocial 目前不支持范围授权令牌,因此在此过程中获得的任何令牌都可以代表你执行所有操作,包括如果你的账户具有管理员权限时的管理员操作。然而,始终以最低权限授予你的应用是一个好习惯。例如,如果你的应用不会发布贴文,请使用 scope=read。
|
GoToSocial 0.19.0 之前的版本并不支持范围授权令牌,因此运行低于 0.19.0 的 GoToSocial 的用户通过此流程获得的任何令牌都可以代表用户执行所有操作。如果用户具有管理权限,那么令牌还可以执行管理操作。
|
||||||
|
|
||||||
本着这种精神,上述示例使用了`read`,这意味着当未来支持范围令牌时,应用将仅限于执行`read`操作。
|
|
||||||
|
|
||||||
你可以在[此处](https://github.com/superseriousbusiness/gotosocial/issues/2232)阅读更多关于计划中 OAuth 安全功能的信息。
|
|
||||||
|
|
||||||
成功调用会返回一个带有 `client_id` 和 `client_secret` 的响应,我们将在后续流程中需要使用这些信息。它看起来像这样:
|
成功调用会返回一个带有 `client_id` 和 `client_secret` 的响应,我们将在后续流程中需要使用这些信息。它看起来像这样:
|
||||||
|
|
||||||
|
|
@ -126,7 +126,6 @@ curl \
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl \
|
curl \
|
||||||
-H 'Content-Type: application/json' \
|
|
||||||
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
|
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
|
||||||
'https://example.org/api/v1/accounts/verify_credentials'
|
'https://example.org/api/v1/accounts/verify_credentials'
|
||||||
```
|
```
|
||||||
|
|
@ -141,7 +140,6 @@ curl \
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl \
|
curl \
|
||||||
-H 'Content-Type: application/json' \
|
|
||||||
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
|
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
|
||||||
'https://example.org/api/v1/notifications'
|
'https://example.org/api/v1/notifications'
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ GoToSocial 使用 [go-swagger](https://github.com/go-swagger/go-swagger) 从代
|
||||||
|
|
||||||
有关更多背景信息,请参见以下问题:
|
有关更多背景信息,请参见以下问题:
|
||||||
|
|
||||||
- [#1958](https://github.com/superseriousbusiness/gotosocial/issues/1958)
|
- [#1958](https://codeberg.org/superseriousbusiness/gotosocial/issues/1958)
|
||||||
- [#1944](https://github.com/superseriousbusiness/gotosocial/issues/1944)
|
- [#1944](https://codeberg.org/superseriousbusiness/gotosocial/issues/1944)
|
||||||
- [#2641](https://github.com/superseriousbusiness/gotosocial/issues/2641)
|
- [#2641](https://codeberg.org/superseriousbusiness/gotosocial/issues/2641)
|
||||||
|
|
||||||
<swagger-ui src="swagger.yaml"/>
|
<swagger-ui src="swagger.yaml"/>
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue