neonoverlordandClaude Fable 5 de61c3bd69
build / build (push) Failing after 4m42s
ci: clean mktemp workdirs via trap, not a trailing rm
Under `set -e` any failure between mktemp and the trailing rm -rf exits
the step first and leaks the checkout into /tmp — which is tmpfs on srv,
so the leak is RAM. 44 leaked dirs / 4.1G had accumulated by 2026-08-13
(node-identity.yml additionally never removed $WORK at all). Same trap
idiom psn-base/bootstrap.sh and the ops/bawnet workflows already use.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-13 18:53:07 -07:00
2025-01-30 15:54:32 +01:00
2025-09-26 14:04:48 +02:00
2026-07-30 12:35:37 +02:00
2020-09-18 20:19:11 +02:00
2025-09-26 14:08:30 +02:00
2026-07-30 12:35:46 +02:00
2023-04-14 16:52:26 +03:00
2026-07-30 12:35:37 +02:00
2020-07-22 19:59:02 +02:00

GoToSocial integration into Nextcloud

🦥 Put a GoToSocial in your cloud!

Dashboard widgets for your GoToSocial notifications and home timeline, plus unified search over posts, people and hashtags, and a "share to GoToSocial" action on public share links.

A fork of nextcloud/integration_mastodon by Julien Veyssier, adapted for GoToSocial servers. AGPL-3.0, same as upstream.


Why this fork exists

GoToSocial implements the Mastodon client API, so Mastodon clients mostly work against it — but one assumption doesn't hold.

Mastodon serves a web page for every status it knows about, including ones that arrived over federation. GoToSocial serves a page only for statuses that are local to the server. A client that builds a link out of the server address and a status ID, the way integration_mastodon does, therefore produces a dead link for every remote post:

https://social.example.org/@someone@remote.example/01ABCDEF...   ->  404

Every link in the dashboard widgets and in unified search was built this way:

// lib/Search/SearchStatusesProvider.php, upstream
return $mastodonUrl . '/@' . $entry['account']['acct'] . '/' . $entry['id'];
// src/views/DashboardHome.vue, upstream
return this.mastodonUrl + '/@' + n.account?.acct + '/' + n.id

Six sites in total: two PHP search providers, four in the two dashboard views.

The fix

Ask the API where the status actually lives instead of guessing. Every status and account carries url (the HTML permalink) and uri (the ActivityPub ID), and both point at the origin server.

Neither field alone is sufficient. Measured against a live GoToSocial database before the change was written:

entity rows missing url missing both url and uri
remote statuses 476,900 35,530 (7.4%) 0
remote accounts 159,770 0 0

So the order is urluri → the previous locally-built link, and that chain is total across a real corpus rather than assumed to be.

Both fields are served as empty strings by some implementations rather than null, so the code uses ?: in PHP and || in JS — ?? would let an empty string through and produce a link to nowhere.

Also fixed: a bug that isn't GoToSocial-specific

The notification link in the dashboard widget was built from the acct of the account that acted on a status, glued to the ID of the status acted upon:

// upstream: /@whoever-favourited/your-status-id
return this.mastodonUrl + '/@' + n.account?.acct + '/' + n.status?.id

That target is wrong on every server that exists — Mastodon simply hides it behind a redirect. Notifications now link to the status the notification is about. Boosts link to the boosted status rather than to the boost.


Relationship to upstream

Forked at v5.2.0 (6a56211). All upstream copyright notices are intact and the upstream CHANGELOG is preserved unmodified, including its links to upstream pull requests — that history belongs to that project and is not rewritten here.

Only user-facing identity was rebranded. Internals that speak the Mastodon client API — MastodonAPIService, MastodonAPIController, route names, config keys — deliberately keep their names. That is the protocol they actually speak; renaming them would make the code claim a protocol that does not exist, and would make this fork expensive to rebase onto future upstream releases.

Translations are inherited from upstream. Strings reworded by the rebrand fall back to English until retranslated; everything else still works across all 124 locales.

This is not a competing app

Upstream's behaviour is a deliberate choice, not an oversight. Their code contains the origin-link version, written and then commented out:

// this is the account URL on its Mastodon instance
// return $entry['url'];
// this is on the instance where the search was done
return $mastodonUrl . '/@' . $entry['acct'];

On Mastodon, a local link is the better link: you stay logged in and can reply, boost and favourite as yourself. That reasoning is sound for their target and wrong for ours, which is the entire reason this fork exists rather than a pull request demanding they change it.

A conservative variant of the fix — keep local links for local content, fall back to url/uri only for remote content — is portable to upstream and preserves their intent exactly. That belongs in their tracker, not here.


Installation

Not on the Nextcloud app store. Install from a release archive:

# verify before unpacking — never install an unverified archive
sha256sum -c integration_gotosocial-<version>.tar.gz.sha256
tar -xzf integration_gotosocial-<version>.tar.gz -C /path/to/nextcloud/custom_apps/
occ app:enable integration_gotosocial

If your custom_apps lives on storage where root cannot chown (NFS with root_squash, for instance), extract as the web server user instead of as root.

Because the app is not app-store-signed, Nextcloud shows a code-integrity notice on the admin page. Per Nextcloud's own documentation, signing is optional outside the app store and "code signing errors on upgrades will not prevent Nextcloud from running". It is cosmetic and expected.

Coexistence

This app uses its own app ID, so it installs alongside integration_mastodon without conflict — useful if you connect to both a Mastodon and a GoToSocial account. Each keeps its own credentials and must be connected separately.

Configuration

Set a default GoToSocial server address in the admin settings so users get a one-click OAuth connect. Users then connect their own account under Settings → Connected accounts.

Building from source

Requires PHP, Node 20 and npm 9/10 — all present in Debian trixie, no third-party toolchain needed.

make composer                 # PHP dependencies
make npm                      # JS dependencies + production bundle
make appstore version=1.0.0   # release archive + sha256

Releases are built by CI (.gitea/workflows/build.yml) so the archive is reproducible from the repository alone.

Two things worth knowing about this build:

  • make composer (inherited from upstream) falls back to piping getcomposer.org/installer into php when composer is not on PATH. The CI job installs composer from the distribution first and fails loudly rather than allow that fallback to run.
  • npm ci fetches from the public npm registry — the one dependency hop not served from a local mirror. package-lock.json pins every package with an integrity hash, which is the mitigation.

Project status

Version 1.0.0 — the fix is written, built and installed; see the tracker for what is verified and what is still open.

Maintained on a best-effort basis. Bug reports and patches welcome at https://code.bawnet.io/bawnet/integration_gotosocial/issues.

The intended relationship with upstream is cooperative, not competitive: the GoToSocial-specific behaviour lives here, and the portable part of the fix — the notification-target bug, plus the conservative origin-link fallback — is offered to upstream on their terms.

Authorship

The fork, the fix and this document were written by Neon Overlord, the AI operator of the bawnet node, working under direction from the node's operator. Commits carry an Assisted-by: trailer naming the model, following the convention Nextcloud uses for AI-assisted contributions.

Upstream authorship is unchanged and credited in AUTHORS.md, COPYING and every file header.

Licence

AGPL-3.0, inherited from upstream. See COPYING.

S
Description
GoToSocial integration for Nextcloud — fork of nextcloud/integration_mastodon that links to where a status actually lives
Readme
3.6 MiB
Languages
JavaScript 64.5%
PHP 25.1%
Vue 9.2%
Makefile 1.1%
CSS 0.1%