diff --git a/.gitignore b/.gitignore index c05e777..bcb2719 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ repos +.claude diff --git a/README.md b/README.md index 661222a..a6bab35 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,12 @@ # at -https://github.com/bluesky-social/atproto +- https://github.com/bluesky-social/atproto +- https://github.com/bluesky-social/atproto/discussions/2026 |word|name|example| |---|---|---| -|at|uri|at://ai.syu.is| -|@|user|@ai.syu.is| +|at|uri|at://example.com| +|@|user|@example.com| |[at]proto|repo|`git@github.com:bluesky-social/atproto`| |[at]mosphere|system|pds, bsky(appview), ozone, bgs, plc| |[a]uthenticated [t]ransfer|protocol|[did](https://www.w3.org/TR/did-core/)| @@ -14,16 +15,13 @@ https://github.com/bluesky-social/atproto ## account -[@ai.syu.is](https://web.syu.is/profile/ai.syu.is) - +- [ai@syu.is](https://syu.is/profile/did:plc:6qyecktefllvenje24fcxnie) +- [ai@bsky.app](https://bsky.app/profile/did:plc:6qyecktefllvenje24fcxnie) - https://plc.syu.is/did:plc:6qyecktefllvenje24fcxnie -- https://plc.directory/did:plc:ytvoptig4ddshmwdsjmhtcym - -[@yui.syui.ai](https://bsky.app/profile/did:plc:4hqjfn7m6n5hno3doamuhgef) +- https://plc.directory/did:plc:6qyecktefllvenje24fcxnie ```sh $ curl -sL syu.is/xrpc/_health -{"version":"0.4.65"} # latest # https://github.com/bluesky-social/atproto/blob/main/packages/pds/package.json @@ -31,10 +29,11 @@ $ curl -sL https://raw.githubusercontent.com/bluesky-social/atproto/refs/heads/m ``` ```sh -$ curl -sL "syu.is/xrpc/com.atproto.repo.describeRepo?repo=ai.syu.is" |jq -r .did +$ handle=ai.syui.ai +$ curl -sL "syu.is/xrpc/com.atproto.repo.describeRepo?repo=${handle}" |jq -r .did did:plc:6qyecktefllvenje24fcxnie -$ curl -sL "syu.is/xrpc/com.atproto.repo.listRecords?repo=ai.syu.is&collection=app.bsky.feed.post&reverse=true&limit=1" +$ curl -sL "syu.is/xrpc/com.atproto.repo.listRecords?repo=${handle}&collection=app.bsky.feed.post&reverse=true&limit=1" {"records":[{"uri":"at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.post/3l6s2riuouk2j","cid":"bafyreibjohl7va4upkibw5twaxdd4jg3l6rmfatu4dpjjfd5xkb2ijtlx4","value":{"text":"hello","$type":"app.bsky.feed.post","langs":["ja"],"createdAt":"2024-10-18T13:21:39.809Z"}}],"cursor":"3l6s2riuouk2j"} ``` @@ -46,80 +45,4 @@ $ curl -sL "syu.is/xrpc/com.atproto.repo.listRecords?repo=ai.syu.is&collection=a - https://feed.syu.is/xrpc/app.bsky.feed.getFeedSkeleton?feed=at://did:plc:4hqjfn7m6n5hno3doamuhgef/app.bsky.feed.generator/cmd - https://desc.syu.is/xrpc/app.bsky.feed.getFeedSkeleton?feed=at://did:plc:6qyecktefllvenje24fcxnie/app.bsky.feed.generator/cmd -## link -- https://github.com/bluesky-social/atproto -- https://github.com/itaru2622/bluesky-selfhost-env -- https://github.com/bluesky-social/atproto/discussions/2026 - -## self-host - -currently, bsky and bsync require patches to function properly. additionally, social-app is not displaying avatars. for components that are not working, it's recommended to use [itaru2622/bluesky-selfhost-env](https://github.com/itaru2622/bluesky-selfhost-env). this repository provides an environment for self-hosting bluesky. - -- bsky = appview -- ozone = mod - -|name|service|patch| -|---|---|---| -|pds|https://github.com/bluesky-social/atproto/blob/main/services/pds/Dockerfile|| -|bsky|https://github.com/bluesky-social/atproto/blob/main/services/bsky/Dockerfile|[itaru2622/bluesky-atproto-bsky](https://github.com/itaru2622/bluesky-selfhost-env/blob/master/patching/105-atproto-services-for-docker.diff)| -|bsync|https://github.com/bluesky-social/atproto/blob/main/services/bsync/Dockerfile|| -|ozone|https://github.com/bluesky-social/atproto/blob/main/services/ozone/Dockerfile|| -|plc|https://github.com/did-method-plc/did-method-plc/tree/main/packages/server|| -|bgs|https://github.com/bluesky-social/indigo/tree/main/cmd/bigsky|| -|feed|https://github.com/bluesky-social/feed-generator|| -|web|https://github.com/bluesky-social/social-app|[bluesky-selfhost-env](https://github.com/itaru2622/bluesky-selfhost-env/blob/master/patching/160-social-app-disable-hackModifyThumbnailPath.diff)| - -```sh -# BSKY_IMG_URI_ENDPOINT, BSKY_BLOB_CACHE_LOC -# avatar link example -1. https://appview.${host}/img/avatar/plain/${did}/${cid}@jpeg -2. https://${host}/xrpc/com.atproto.sync.getBlob?did=${did}&cid=${cid} -``` - -docker compose will not be published unless you write ports. it is only valid internally. add ports only for what you want to publish. - -## api - -```sh -# create account -url=https://${pds}/xrpc/com.atproto.server.createAccount -json="{\"email\": \"$email\", \"handle\": \"$handle\", \"password\": \"$password\"}" -curl -X POST -H "Content-Type: application/json" -d $json -sL $url -``` - -change `src/pds/handle` to use a name of 3 characters or less. also, you cannot create an account with a name of 3 characters or less from social-app (web client). please create it from api. - -- [/atproto/packages/pds/src/handle/index.ts](https://github.com/bluesky-social/atproto/blame/d4d5a6edba972c0e9976289bde8bc0b42ff547ca/packages/pds/src/handle/index.ts#L86-L88) - -```sh -# invite code -admin_password=xxx -url=https://$host/xrpc/com.atproto.server.createInviteCode -json="{\"useCount\":1}" -curl -X POST -u admin:${admin_password} -H "Content-Type: application/json" -d "$json" -sL $url -``` - -## oauth - -```sh -# https://github.com/bluesky-social/cookbook/tree/main/python-oauth-web-app -$ cd ./repos/cookbook/python-oauth-web-app -$ rye sync -$ rye run python3 -c 'import secrets; print(secrets.token_hex())'|xargs echo FLASK_SECRET_KEY|tr -d ' ' >> .env -$ rye run python3 generate_jwk.py |xargs echo FLASK_CLIENT_SECRET_JWK|tr -d ' ' >> .env -$ cat .env -$ rye run flask run -``` - -please access `127.0.0.1:5000`. it may not work if you use localhost. - -also, oauth doesn't work on localhost. use [ngrok](https://ngrok.com/), [tailscale](https://tailscale.com/), [cloudflare](https://github.com/cloudflare/cloudflared). - -```sh -$ ngrok http http://localhost:5000 -``` - -```sh -$ cloudflared tunnel --url http://localhost:5000 -``` diff --git a/compose.yml b/compose.yml index 813393a..d3693c4 100644 --- a/compose.yml +++ b/compose.yml @@ -48,10 +48,30 @@ services: - ./envs/pds volumes: - ./data/pds/:/data/ + command: node --enable-source-maps index.js depends_on: database: condition: service_healthy + bsky: + ports: + - 2584:2584 + build: + context: ./repos/atproto/ + dockerfile: services/bsky/Dockerfile + restart: always + env_file: + - ./envs/bsky + user: root + volumes: + - ./data/bsky/:/data/ + command: node --enable-source-maps api.js + depends_on: + database: + condition: service_healthy + redis: + condition: service_healthy + bgs: ports: - 2470:2470 @@ -78,24 +98,6 @@ services: - ./envs/social-app command: "/usr/bin/bskyweb serve" - bsky: - ports: - - 2584:2584 - build: - context: ./repos/atproto/ - dockerfile: services/bsky/Dockerfile - restart: always - env_file: - - ./envs/bsky - user: root - volumes: - - ./data/bsky/:/data/ - command: node --enable-source-maps api.js - depends_on: - database: - condition: service_healthy - redis: - condition: service_healthy jetstream: build: diff --git a/envs/bgs b/envs/bgs index fa79239..a5097f3 100644 --- a/envs/bgs +++ b/envs/bgs @@ -2,5 +2,5 @@ DATABASE_URL=postgres://postgres:postgres@database/bgs CARSTORE_DATABASE_URL=postgres://postgres:postgres@database/carstore DATA_DIR=/data ATP_PLC_HOST=https://plc.${host} - -BGS_ADMIN_KEY +BGS_NEW_PDS_PER_DAY_LIMIT=1000 +BGS_ADMIN_KEY= diff --git a/envs/pds b/envs/pds index fabc2a4..b2f0241 100644 --- a/envs/pds +++ b/envs/pds @@ -6,6 +6,8 @@ PDS_BLOBSTORE_DISK_LOCATION=/data/img/static PDS_BSKY_APP_VIEW_DID=did:web:bsky.${host} PDS_BSKY_APP_VIEW_URL=https://bsky.${host} PDS_CRAWLERS=https://bgs.${host} +PDS_SEQUENCER_ENABLED=true +PDS_SEQUENCER_DB_LOCATION=/data/sequencer.sqlite PDS_DEV_MODE=true PDS_DID_PLC_URL=https://plc.${host} PDS_ENABLE_DID_DOC_WITH_SESSION=true diff --git a/envs/social-app b/envs/social-app index a6752de..a1bc28f 100644 --- a/envs/social-app +++ b/envs/social-app @@ -1 +1,3 @@ -ATP_APPVIEW_HOST=https://bsky.${host} +ATP_APPVIEW_HOST=https://public.api.bsky.app +EXPO_PUBLIC_BLUESKY_PROXY_DID=did:web:api.bsky.app +EXPO_PUBLIC_ENV=production diff --git a/install.zsh b/install.zsh index 94339c8..afd090b 100755 --- a/install.zsh +++ b/install.zsh @@ -18,6 +18,7 @@ function at-repos-env() { https://github.com/bluesky-social/ozone https://github.com/bluesky-social/jetstream ) + services=( bsky plc pds jetstream bgs ozone social-app ) d=${0:a:h} dh=${0:a:h:h} name=${host%%.*} @@ -73,6 +74,7 @@ function at-repos-pull() { echo $repo if [ -d $d/repos/${repo##*/} ];then cd $d/repos/${repo##*/} + git stash if ! git pull;then rm -rf $d/repos/${repo##*/} at-repos-clone @@ -100,15 +102,60 @@ function at-repos-social-app-icon-origin() { curl -sL $icon -o $d/icons/Logotype.tsx } -function at-repos-social-app-write() { +function at-repos-social-app-avatar-write() { did_admin=did:plc:6qyecktefllvenje24fcxnie - dt=$d/repos/social-app/src cd $dt grep -R syu.is .|cut -d : -f 1|sort -u|xargs sed -i "s/syu.is/${host}/g" grep -R web.syu.is .|cut -d : -f 1|sort -u|xargs sed -i "s/web.syu.is/web.${host}/g" f=$dt/lib/constants.ts - sed -i "s/public.api.web/bsky/g" $f + sed -i "s#export const BSKY_SERVICE = 'https://bsky.social'#export const BSKY_SERVICE = 'https://${host}'#g" $f + sed -i "s#export const BSKY_SERVICE_DID = 'did:web:bsky.social'#export const BSKY_SERVICE_DID = 'did:web:${host}'#g" $f + sed -i "s#export const PUBLIC_BSKY_SERVICE = 'https://public.api.bsky.app'#export const PUBLIC_BSKY_SERVICE = 'https://bsky.${host}'#g" $f + sed -i "s#export const PUBLIC_APPVIEW = 'https://api.bsky.app'#export const PUBLIC_APPVIEW = 'https://bsky.${host}'#g" $f + sed -i "s#export const PUBLIC_APPVIEW_DID = 'did:web:api.bsky.app'#export const PUBLIC_APPVIEW_DID = 'did:web:bsky.${host}'#g" $f + + # Disable external services (CORS fix) + f=$dt/state/geolocation/const.ts + curl -sL https://raw.githubusercontent.com/bluesky-social/social-app/refs/heads/main/src/state/geolocation/const.ts -o $f + cat > $f << 'GEOEOF' +import {type GeolocationStatus} from '#/state/geolocation/types' +import {BAPP_CONFIG_DEV_URL, IS_DEV} from '#/env' +import {type Device} from '#/storage' + +export const IPCC_URL = `https://bsky.app/ipcc` +// Disabled for self-hosted environment to avoid CORS errors +export const BAPP_CONFIG_URL_PROD = null +export const BAPP_CONFIG_URL = null +export const GEOLOCATION_CONFIG_URL = BAPP_CONFIG_URL + +export const DEFAULT_GEOLOCATION_CONFIG: Device['geolocation'] = { + countryCode: undefined, + regionCode: undefined, + ageRestrictedGeos: [], + ageBlockedGeos: [], +} + +export const DEFAULT_GEOLOCATION_STATUS: GeolocationStatus = { + countryCode: undefined, + regionCode: undefined, + isAgeRestrictedGeo: false, + isAgeBlockedGeo: false, +} +GEOEOF + + # Add null check to geolocation config.ts to prevent fetch(null) errors + f=$dt/state/geolocation/config.ts + curl -sL https://raw.githubusercontent.com/bluesky-social/social-app/refs/heads/main/src/state/geolocation/config.ts -o $f + # Add null check at the beginning of getGeolocationConfig function (after line with 'url: string,') + sed -i "s/): Promise {/): Promise {\n if (!url) return undefined/" $f + + # Disable Statsig (CORS fix) + f=$dt/lib/statsig/statsig.tsx + sed -i "s#api: 'https://events.bsky.app/v2'#api: '' // Disabled for self-hosted#g" $f + # Disable SDK initialization to prevent statsigapi.net connections + sed -i "s#const SDK_KEY = 'client-SXJakO39w9vIhl3D44u8UupyzFl4oZ2qPIkjwcvuPsV'#const SDK_KEY = '' // Disabled for self-hosted#g" $f + f=$dt/view/icons/Logotype.tsx o=$d/icons/Logotype.tsx cp -rf $o $f @@ -123,31 +170,53 @@ function at-repos-social-app-write() { grep -R $did_admin .|cut -d : -f 1|sort -u|xargs sed -i "s/${did_admin}/${did}/g" } -function at-repos-bsky-patch() { +function at-repos-atproto-service-bsky-api-patch() { + # https://github.com/itaru2622/bluesky-selfhost-env/blob/master/patching/105-atproto-services-for-docker.diff f=$d/repos/atproto/services/bsky/api.js curl -sL https://raw.githubusercontent.com/bluesky-social/atproto/refs/heads/main/services/bsky/api.js -o $f d_=$d/repos/atproto - p_=$d/patching/105-atproto-services-for-docker.diff - cd ${d_} - if [ ! -f ${p_} ];then - # https://github.com/itaru2622/bluesky-selfhost-env/blob/master/patching/105-atproto-services-for-docker.diff - echo download patch: https://github.com/itaru2622/bluesky-selfhost-env/blob/master/patching/105-atproto-services-for-docker.diff - curl -sL https://raw.githubusercontent.com/itaru2622/bluesky-selfhost-env/refs/heads/master/patching/105-atproto-services-for-docker.diff -o ${p_} - else - echo local patch - fi + p_=$d/patching/4367-atproto-services-bsky-api.diff echo "applying patch: under ${f} for ${p_}" pushd ${d_} patch -p1 < ${p_} popd } -function at-repos-social-app-patch() { - f=$d/repos/social-app/Dockerfile - p_=$d/patching/social-app-dockerfile.diff +function at-repos-atproto-service-pds-index-patch() { + f=$d/repos/atproto/services/pds/index.js + curl -sL https://raw.githubusercontent.com/bluesky-social/atproto/refs/heads/main/services/pds/index.js -o $f + d_=$d/repos/atproto + p_=$d/patching/4367-atproto-services-pds-index.diff + echo "applying patch: under ${f} for ${p_}" + pushd ${d_} + patch -p1 < ${p_} + popd +} + +function at-repos-social-app-agent-patch() { + f=$d/repos/social-app/src/state/session/agent.ts + p_=$d/patching/8980-social-app-disable-proxy.diff d_=$d/repos/social-app - cd ${d_} - curl -sLO https://raw.githubusercontent.com/bluesky-social/social-app/refs/heads/main/Dockerfile + echo "applying patch: under ${f} for ${p_}" + pushd ${d_} + patch -p1 < ${p_} + popd +} + +function at-repos-social-app-disable-external-services-patch() { + f=$d/repos/social-app/src/state/geolocation/const.ts + p_=$d/patching/8980-social-app-disable-external-services.diff + d_=$d/repos/social-app + echo "applying patch: under ${f} for ${p_}" + pushd ${d_} + patch -p1 < ${p_} + popd +} + +function at-repos-atproto-service-ozone-api-patch() { + f=$d/repos/atproto/services/ozone/api.js + d_=$d/repos/atproto + p_=$d/patching/130-atproto-ozone-enable-daemon.patch echo "applying patch: under ${f} for ${p_}" pushd ${d_} patch -p1 < ${p_} @@ -172,52 +241,91 @@ function at-repos-ozone-patch() { pushd ${d_} patch -p1 < ${p_} popd - #cp -rf $d/repos/atproto/service/ozone/* $d/ozone/service/ } -function at-repos-docker() { +function at-repos-build-docker-atproto() { cd $d - docker compose build - # docker compose up -d - # docker compose up -d --no-build - # docker compose up -d --pull always + docker image prune -a + if [ -z "$1" ];then + for ((i=1; i<=${#services}; i++)); do + service=${services[$i]} + docker compose build --no-cache $service + done + else + docker compose build --no-cache $1 + fi } -function at-regi-docker() { - docker run -d -p ${dport}:${dport} --name registry --restart=always registry:2 - docker tag at-pds:latest localhost:${dport}/pds:latest - docker tag at-ozone-web:latest localhost:${dport}/ozone-web:latest - docker tag at-bgs:latest localhost:${dport}/bgs:latest - docker tag at-jetstream:latest localhost:${dport}/jetstream:latest - docker tag at-bsky:latest localhost:${dport}/bsky:latest - docker tag at-ozone-daemon:latest localhost:${dport}/ozone-daemon:latest - docker tag at-ozone:latest localhost:${dport}/ozone:latest - docker tag at-plc:latest localhost:${dport}/plc:latest - docker tag at-social-app:latest localhost:${dport}/social-app:latest - - docker push localhost:${dport}/pds:latest - docker push localhost:${dport}/ozone-web:latest - docker push localhost:${dport}/bgs:latest - docker push localhost:${dport}/jetstream:latest - docker push localhost:${dport}/bsky:latest - docker push localhost:${dport}/ozone-daemon:latest - docker push localhost:${dport}/ozone:latest - docker push localhost:${dport}/plc:latest - docker push localhost:${dport}/social-app:latest - +function at-repos-push-reset() { docker restart registry + docker stop registry + docker rm registry + docker volume rm registry-data 2>/dev/null || true + docker run -d -p ${dport}:${dport} --name registry \ + --restart=always \ + -v registry-data:/var/lib/registry \ + registry:2 + sleep 3 + docker run -d -p ${dport}:${dport} --name registry --restart=always registry:2 +} + +function at-repos-push-docker() { + if [ -z "$1" ];then + for ((i=1; i<=${#services}; i++)); do + service=${services[$i]} + docker tag at-${service}:latest localhost:${dport}/${service}:latest + docker push localhost:${dport}/${service}:latest + if [ "$service" = "ozone" ];then + docker tag at-${service}:latest localhost:${dport}/${service}-web:latest + docker push localhost:${dport}/${service}-web:latest + docker tag at-${service}:latest localhost:${dport}/${service}-daemon:latest + docker push localhost:${dport}/${service}-daemon:latest + fi + done + else + docker tag at-${1}:latest localhost:${dport}/${1}:latest + docker push localhost:${dport}/${1}:latest + fi +} + +function at-repos-pull-docker() { + cd $d + docker image prune -a + docker compose up -d --pull always +} + +function at-origin-social-app() { + cp -rf $d/social-app-custom $d/repos/social-app } at-repos-env -at-repos-clone -at-repos-pull -at-repos-social-app-icon -at-repos-social-app-icon-origin -at-repos-social-app-write -at-repos-bsky-patch -at-repos-social-app-patch -at-repos-ozone-patch -at-repos-docker +case "`cat /etc/hostname`" in + at) + at-repos-pull-docker + exit + ;; + *) + at-repos-push-reset + at-repos-clone + at-repos-pull + at-repos-social-app-icon + at-repos-social-app-icon-origin + at-repos-social-app-avatar-write + at-repos-social-app-agent-patch + at-repos-social-app-disable-external-services-patch + at-repos-atproto-service-bsky-api-patch + at-repos-atproto-service-pds-index-patch + at-repos-atproto-service-ozone-api-patch + at-repos-ozone-patch + if [ -n "$1" ];then + at-repos-build-docker-atproto $1 + at-repos-push-docker $1 + exit + fi + at-repos-build-docker-atproto + at-repos-push-docker + cd $d; docker compose down + ;; +esac -# at-regi-docker diff --git a/patching/130-atproto-ozone-enable-daemon.patch b/patching/130-atproto-ozone-enable-daemon.patch new file mode 100644 index 0000000..7a10d3d --- /dev/null +++ b/patching/130-atproto-ozone-enable-daemon.patch @@ -0,0 +1,33 @@ +--- a/services/ozone/api.js ++++ b/services/ozone/api.js +@@ -20,6 +20,7 @@ const { + MultiImageInvalidator, + } = require('@atproto/aws') + const { + Database, + OzoneService, ++ OzoneDaemon, + envToCfg, + envToSecrets, + httpLogger, +@@ -76,10 +77,17 @@ const main = async () => { + const ozone = await OzoneService.create(cfg, secrets, { imgInvalidator }) + + await ozone.start() + + httpLogger.info('ozone is running') + ++ // Start OzoneDaemon for label events ++ httpLogger.info('starting ozone daemon') ++ const daemon = await OzoneDaemon.create(cfg, secrets) ++ await daemon.start() ++ httpLogger.info('ozone daemon is running') ++ + // Graceful shutdown (see also https://aws.amazon.com/blogs/containers/graceful-shutdowns-with-ecs/) + process.on('SIGTERM', async () => { + httpLogger.info('ozone is stopping') + ++ await daemon.destroy() + await ozone.destroy() + + httpLogger.info('ozone is stopped') diff --git a/patching/105-atproto-services-for-docker.diff b/patching/4367-atproto-services-bsky-api.diff similarity index 52% rename from patching/105-atproto-services-for-docker.diff rename to patching/4367-atproto-services-bsky-api.diff index c428d6f..f887230 100644 --- a/patching/105-atproto-services-for-docker.diff +++ b/patching/4367-atproto-services-bsky-api.diff @@ -1,14 +1,89 @@ -diff --git a/services/bsky/api.js b/services/bsky/api.js -index 56c769b9d..5d14c0057 100644 ---- a/services/bsky/api.js -+++ b/services/bsky/api.js -@@ -44,19 +44,62 @@ const assert = require('node:assert') +--- a/services/bsky/api.js 2025-12-03 11:04:54 ++++ b/services/bsky/api.js 2025-12-03 11:00:02 +@@ -1,62 +1,105 @@ + /* eslint-env node */ + /* eslint-disable import/order */ +- ++// https://github.com/bluesky-social/atproto/blob/main/services/bsky/api.js + 'use strict' + +-const dd = require('dd-trace') ++//const dd = require('dd-trace') ++// ++//dd.tracer ++// .init() ++// .use('http2', { ++// client: true, // calls into dataplane ++// server: false, ++// }) ++// .use('express', { ++// hooks: { ++// request: (span, req) => { ++// maintainXrpcResource(span, req) ++// }, ++// }, ++// }) + +-dd.tracer +- .init() +- .use('http2', { +- client: true, // calls into dataplane +- server: false, +- }) +- .use('express', { +- hooks: { +- request: (span, req) => { +- maintainXrpcResource(span, req) +- }, +- }, +- }) +- + // modify tracer in order to track calls to dataplane as a service with proper resource names + const DATAPLANE_PREFIX = '/bsky.Service/' +-const origStartSpan = dd.tracer._tracer.startSpan +-dd.tracer._tracer.startSpan = function (name, options) { +- if ( +- name !== 'http.request' || +- options?.tags?.component !== 'http2' || +- !options?.tags?.['http.url'] +- ) { +- return origStartSpan.call(this, name, options) +- } +- const uri = new URL(options.tags['http.url']) +- if (!uri.pathname.startsWith(DATAPLANE_PREFIX)) { +- return origStartSpan.call(this, name, options) +- } +- options.tags['service.name'] = 'dataplane-bsky' +- options.tags['resource.name'] = uri.pathname.slice(DATAPLANE_PREFIX.length) +- return origStartSpan.call(this, name, options) +-} ++//const origStartSpan = dd.tracer._tracer.startSpan ++//dd.tracer._tracer.startSpan = function (name, options) { ++// if ( ++// name !== 'http.request' || ++// options?.tags?.component !== 'http2' || ++// !options?.tags?.['http.url'] ++// ) { ++// return origStartSpan.call(this, name, options) ++// } ++// const uri = new URL(options.tags['http.url']) ++// if (!uri.pathname.startsWith(DATAPLANE_PREFIX)) { ++// return origStartSpan.call(this, name, options) ++// } ++// options.tags['service.name'] = 'dataplane-bsky' ++// options.tags['resource.name'] = uri.pathname.slice(DATAPLANE_PREFIX.length) ++// return origStartSpan.call(this, name, options) ++//} + + // Tracer code above must come before anything else + const assert = require('node:assert') const cluster = require('node:cluster') const path = require('node:path') - + -const { BskyAppView, ServerConfig } = require('@atproto/bsky') -+const bsky = require('@atproto/bsky') // import all bsky features - const { Secp256k1Keypair } = require('@atproto/crypto') +-const { Secp256k1Keypair } = require('@atproto/crypto') ++const bsky = require('/app/packages/bsky') // import all bsky features ++const { Secp256k1Keypair } = require('/app/packages/crypto') const main = async () => { const env = getEnv() @@ -70,7 +145,7 @@ index 56c769b9d..5d14c0057 100644 } process.on('SIGTERM', shutdown) process.on('disconnect', shutdown) // when clustering -@@ -64,6 +107,12 @@ const main = async () => { +@@ -64,6 +107,12 @@ const getEnv = () => ({ serviceSigningKey: process.env.BSKY_SERVICE_SIGNING_KEY || undefined, diff --git a/patching/4367-atproto-services-pds-index.diff b/patching/4367-atproto-services-pds-index.diff new file mode 100644 index 0000000..a8165f8 --- /dev/null +++ b/patching/4367-atproto-services-pds-index.diff @@ -0,0 +1,20 @@ +--- a/services/pds/index.js 2025-12-03 11:04:54 ++++ b/services/pds/index.js 2025-12-02 22:11:39 +@@ -1,5 +1,5 @@ + /* eslint-env node */ +- ++// https://github.com/bluesky-social/atproto/blob/main/services/pds/index.js + 'use strict' + + const { +@@ -8,8 +8,8 @@ + envToSecrets, + httpLogger, + readEnv, +-} = require('@atproto/pds') +-const pkg = require('@atproto/pds/package.json') ++} = require('/app/packages/pds') ++const pkg = require('/app/packages/pds/package.json') + + const main = async () => { + const env = readEnv() diff --git a/patching/8980-social-app-disable-external-services.diff b/patching/8980-social-app-disable-external-services.diff new file mode 100644 index 0000000..74ffc22 --- /dev/null +++ b/patching/8980-social-app-disable-external-services.diff @@ -0,0 +1,17 @@ +--- a/src/state/geolocation/const.ts ++++ b/src/state/geolocation/const.ts +@@ -3,9 +3,10 @@ import {BAPP_CONFIG_DEV_URL, IS_DEV} from '#/env' + import {type Device} from '#/storage' + + export const IPCC_URL = `https://bsky.app/ipcc` +-export const BAPP_CONFIG_URL_PROD = `https://ip.bsky.app/config` +-export const BAPP_CONFIG_URL = IS_DEV +- ? (BAPP_CONFIG_DEV_URL ?? BAPP_CONFIG_URL_PROD) +- : BAPP_CONFIG_URL_PROD ++// Disabled for self-hosted environment to avoid CORS errors ++// export const BAPP_CONFIG_URL_PROD = `https://ip.bsky.app/config` ++// export const BAPP_CONFIG_URL = IS_DEV ++// ? (BAPP_CONFIG_DEV_URL ?? BAPP_CONFIG_URL_PROD) ++// : BAPP_CONFIG_URL_PROD ++export const BAPP_CONFIG_URL = null + export const GEOLOCATION_CONFIG_URL = BAPP_CONFIG_URL diff --git a/patching/8980-social-app-disable-proxy.diff b/patching/8980-social-app-disable-proxy.diff new file mode 100644 index 0000000..4bc454f --- /dev/null +++ b/patching/8980-social-app-disable-proxy.diff @@ -0,0 +1,44 @@ +diff --git a/src/state/session/agent.ts b/src/state/session/agent.ts +index 36d19299b..ba095436a 100644 +--- a/src/state/session/agent.ts ++++ b/src/state/session/agent.ts +@@ -39,7 +39,8 @@ export function createPublicAgent() { + configureModerationForGuest() // Side effect but only relevant for tests + + const agent = new BskyAppAgent({service: PUBLIC_BSKY_SERVICE}) +- agent.configureProxy(BLUESKY_PROXY_HEADER.get()) ++ // Disable proxy for self-hosted environments ++ // agent.configureProxy(BLUESKY_PROXY_HEADER.get()) + return agent + } + +@@ -77,7 +78,8 @@ export async function createAgentAndResume( + } + } + +- agent.configureProxy(BLUESKY_PROXY_HEADER.get()) ++ // Disable proxy for self-hosted environments ++ // agent.configureProxy(BLUESKY_PROXY_HEADER.get()) + + return agent.prepare(gates, moderation, onSessionChange) + } +@@ -112,7 +114,8 @@ export async function createAgentAndLogin( + const gates = tryFetchGates(account.did, 'prefer-fresh-gates') + const moderation = configureModerationForAccount(agent, account) + +- agent.configureProxy(BLUESKY_PROXY_HEADER.get()) ++ // Disable proxy for self-hosted environments ++ // agent.configureProxy(BLUESKY_PROXY_HEADER.get()) + + return agent.prepare(gates, moderation, onSessionChange) + } +@@ -201,7 +204,8 @@ export async function createAgentAndCreateAccount( + logger.error(e, {message: `session: failed snoozeEmailConfirmationPrompt`}) + } + +- agent.configureProxy(BLUESKY_PROXY_HEADER.get()) ++ // Disable proxy for self-hosted environments ++ // agent.configureProxy(BLUESKY_PROXY_HEADER.get()) + + return agent.prepare(gates, moderation, onSessionChange) + } diff --git a/patching/social-app-dockerfile.diff b/patching/social-app-dockerfile.diff deleted file mode 100644 index 816cf15..0000000 --- a/patching/social-app-dockerfile.diff +++ /dev/null @@ -1,10 +0,0 @@ ---- a/Dockerfile -+++ b/Dockerfile -@@ -37,6 +37,7 @@ RUN \. "$NVM_DIR/nvm.sh" && \ - echo "EXPO_PUBLIC_BUNDLE_IDENTIFIER=$EXPO_PUBLIC_BUNDLE_IDENTIFIER" >> .env && \ - echo "EXPO_PUBLIC_BUNDLE_DATE=$(date -u +"%y%m%d%H")" >> .env && \ - npm install --global yarn && \ -+ yarn config set network-timeout 600000 && \ - yarn && \ - yarn intl:build && \ - EXPO_PUBLIC_BUNDLE_IDENTIFIER=$EXPO_PUBLIC_BUNDLE_IDENTIFIER EXPO_PUBLIC_BUNDLE_DATE=$() yarn build-web diff --git a/social-app-custom/.keep b/social-app-custom/.keep new file mode 100644 index 0000000..e69de29 diff --git a/social-app-custom/AppInfo.tsx b/social-app-custom/AppInfo.tsx new file mode 100644 index 0000000..6242b12 --- /dev/null +++ b/social-app-custom/AppInfo.tsx @@ -0,0 +1,134 @@ +import React from 'react' +import {View, Text, StyleSheet, Pressable, Linking} from 'react-native' + +interface AppInfoProps { + onLinkPress?: (url: string) => void +} + +export default function AppInfo({onLinkPress}: AppInfoProps) { + const handleLinkPress = (url: string) => { + if (onLinkPress) { + onLinkPress(url) + } else { + Linking.openURL(url) + } + } + + return ( + + + About This App + + This is a customized AT Protocol social networking client. It allows you to + connect to any Personal Data Server (PDS) and participate in the decentralized + social network. + + + + + Key Features + + • Connect to any AT Protocol PDS + • Post text, images, and videos + • Follow users and view timelines + • Customize feeds and moderation settings + • Direct messaging support + + + + + Open Source + + This application is based on the Bluesky social-app, licensed under the MIT + License. The original source code is available at: + + + handleLinkPress('https://github.com/bluesky-social/social-app') + }> + github.com/bluesky-social/social-app + + + + + AT Protocol + + This app uses the AT Protocol (Authenticated Transfer Protocol), an open and + decentralized standard for social applications. + + handleLinkPress('https://atproto.com')}> + atproto.com + + + + + License + + Copyright 2023–2025 Bluesky Social PBC + + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software. + + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. + + + + + Contact + handleLinkPress('https://syu.is')}> + https://syu.is + + + + + Version 1.0.0 + + + ) +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + section: { + marginBottom: 24, + }, + sectionTitle: { + fontSize: 20, + fontWeight: '600', + color: '#1d1d1f', + marginBottom: 12, + }, + paragraph: { + fontSize: 15, + lineHeight: 22, + color: '#3a3a3c', + marginBottom: 8, + }, + list: { + marginLeft: 8, + marginTop: 8, + }, + listItem: { + fontSize: 15, + lineHeight: 24, + color: '#3a3a3c', + }, + link: { + fontSize: 15, + color: '#007aff', + textDecorationLine: 'underline', + marginTop: 8, + }, + versionText: { + fontSize: 13, + color: '#8e8e93', + fontStyle: 'italic', + }, +}) diff --git a/social-app-custom/LicenseNotice.tsx b/social-app-custom/LicenseNotice.tsx new file mode 100644 index 0000000..496d367 --- /dev/null +++ b/social-app-custom/LicenseNotice.tsx @@ -0,0 +1,95 @@ +import React from 'react' +import {View, Text, StyleSheet, Pressable, Linking} from 'react-native' + +export default function LicenseNotice() { + return ( + + Open Source Licenses + + + Bluesky Social App + MIT License + Copyright 2023–2025 Bluesky Social PBC + + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + + + Linking.openURL('https://github.com/bluesky-social/social-app') + }> + View Source Code + + + + ) +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + padding: 16, + }, + title: { + fontSize: 24, + fontWeight: 'bold', + marginBottom: 20, + color: '#1d1d1f', + }, + section: { + marginBottom: 24, + padding: 16, + backgroundColor: '#f5f5f7', + borderRadius: 8, + }, + projectName: { + fontSize: 18, + fontWeight: '600', + marginBottom: 8, + color: '#1d1d1f', + }, + license: { + fontSize: 14, + fontWeight: '500', + color: '#007aff', + marginBottom: 4, + }, + copyright: { + fontSize: 13, + color: '#3a3a3c', + marginBottom: 12, + }, + licenseText: { + fontSize: 12, + lineHeight: 18, + color: '#3a3a3c', + marginBottom: 12, + }, + link: { + fontSize: 14, + color: '#007aff', + textDecorationLine: 'underline', + marginTop: 8, + }, +}) diff --git a/social-app-custom/PrivacyContent.tsx b/social-app-custom/PrivacyContent.tsx new file mode 100644 index 0000000..d780104 --- /dev/null +++ b/social-app-custom/PrivacyContent.tsx @@ -0,0 +1,163 @@ +import React from 'react' +import {View, Text, StyleSheet, Pressable, Linking} from 'react-native' + +interface PrivacyContentProps { + onLinkPress?: (url: string) => void +} + +export default function PrivacyContent({onLinkPress}: PrivacyContentProps) { + const handleLinkPress = (url: string) => { + if (onLinkPress) { + onLinkPress(url) + } else { + Linking.openURL(url) + } + } + + return ( + + + Introduction + + This Privacy Policy explains how this AT Protocol client application + (hereinafter referred to as "the App") handles personal information. + Please read this policy carefully before using the App. + + + + + Information We Collect + + The App may collect and use the following information: + + + 1. Information Collected Automatically + + • Device information (model, OS version) + • App usage data (sessions, features used) + • Crash logs and performance data + + + 2. Information Provided by Users + + + • DID (Decentralized Identifier) and handle for authentication + + • Posts, media, and social interactions + • Profile information (avatar, display name, bio) + + + + + Important: Your data is stored on your chosen PDS (Personal Data Server). + This app does not store your content on our servers. + + + + + + How We Use Your Information + + + • To provide AT Protocol social networking features + + • To improve app performance and user experience + • To diagnose and fix technical issues + + + + + Data Sharing + + The App interacts with your chosen PDS and AppView services. Your posts and + profile information are shared according to the AT Protocol specification and + your privacy settings. + + + + + Your Rights + + You have the right to access, modify, or delete your data through your PDS. + You can also switch to a different PDS at any time while maintaining your + identity. + + + + + Contact + + For questions about this Privacy Policy, please contact: + + handleLinkPress('https://syu.is')}> + https://syu.is + + + + + Last Updated: December 3, 2025 + + + ) +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + section: { + marginBottom: 24, + }, + sectionTitle: { + fontSize: 20, + fontWeight: '600', + color: '#1d1d1f', + marginBottom: 12, + }, + subTitle: { + fontSize: 16, + fontWeight: '500', + color: '#1d1d1f', + marginTop: 12, + marginBottom: 8, + }, + paragraph: { + fontSize: 15, + lineHeight: 22, + color: '#3a3a3c', + marginBottom: 8, + }, + list: { + marginLeft: 8, + marginTop: 8, + }, + listItem: { + fontSize: 15, + lineHeight: 24, + color: '#3a3a3c', + }, + highlight: { + backgroundColor: '#fff3cd', + borderLeftWidth: 4, + borderLeftColor: '#ffc107', + padding: 12, + marginTop: 12, + borderRadius: 4, + }, + highlightText: { + fontSize: 14, + lineHeight: 20, + color: '#856404', + }, + link: { + fontSize: 15, + color: '#007aff', + textDecorationLine: 'underline', + marginTop: 8, + }, + lastUpdated: { + fontSize: 13, + color: '#8e8e93', + fontStyle: 'italic', + }, +}) diff --git a/social-app-custom/PrivacyPolicy.screen.tsx b/social-app-custom/PrivacyPolicy.screen.tsx new file mode 100644 index 0000000..df5d5c8 --- /dev/null +++ b/social-app-custom/PrivacyPolicy.screen.tsx @@ -0,0 +1,42 @@ +import React from 'react' +import {View} from 'react-native' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {useFocusEffect} from '@react-navigation/native' + +import {usePalette} from '#/lib/hooks/usePalette' +import { + type CommonNavigatorParams, + type NativeStackScreenProps, +} from '#/lib/routes/types' +import {s} from '#/lib/styles' +import {useSetMinimalShellMode} from '#/state/shell' +import {ScrollView} from '#/view/com/util/Views' +import * as Layout from '#/components/Layout' +import {ViewHeader} from '../com/util/ViewHeader' +import PrivacyContent from '#/components/custom/PrivacyContent' + +type Props = NativeStackScreenProps +export const PrivacyPolicyScreen = (_props: Props) => { + const pal = usePalette('default') + const {_} = useLingui() + const setMinimalShellMode = useSetMinimalShellMode() + + useFocusEffect( + React.useCallback(() => { + setMinimalShellMode(false) + }, [setMinimalShellMode]), + ) + + return ( + + + + + + + + + + ) +} diff --git a/social-app-custom/Support.screen.tsx b/social-app-custom/Support.screen.tsx new file mode 100644 index 0000000..3fddf92 --- /dev/null +++ b/social-app-custom/Support.screen.tsx @@ -0,0 +1,38 @@ +import React from 'react' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {useFocusEffect} from '@react-navigation/native' + +import {usePalette} from '#/lib/hooks/usePalette' +import { + type CommonNavigatorParams, + type NativeStackScreenProps, +} from '#/lib/routes/types' +import {s} from '#/lib/styles' +import {useSetMinimalShellMode} from '#/state/shell' +import {ViewHeader} from '#/view/com/util/ViewHeader' +import {ScrollView} from '#/view/com/util/Views' +import * as Layout from '#/components/Layout' +import AppInfo from '#/components/custom/AppInfo' + +type Props = NativeStackScreenProps +export const SupportScreen = (_props: Props) => { + const pal = usePalette('default') + const setMinimalShellMode = useSetMinimalShellMode() + const {_} = useLingui() + + useFocusEffect( + React.useCallback(() => { + setMinimalShellMode(false) + }, [setMinimalShellMode]), + ) + + return ( + + + + + + + ) +} diff --git a/social-app-custom/app.config.patch.js b/social-app-custom/app.config.patch.js new file mode 100644 index 0000000..136a4d9 --- /dev/null +++ b/social-app-custom/app.config.patch.js @@ -0,0 +1,9 @@ +// Aiat app configuration overrides +module.exports = { + name: 'Aiat', + slug: 'aiat', + scheme: 'aiat', + owner: 'syui', // Your Expo account + bundleIdentifier: 'ai.syui.at', + // Icon will be set separately +} diff --git a/social-app-custom/bin/build.zsh b/social-app-custom/bin/build.zsh new file mode 100755 index 0000000..d1fe8ea --- /dev/null +++ b/social-app-custom/bin/build.zsh @@ -0,0 +1,44 @@ +#!/bin/zsh +set -e + +d=~/ai/at/repos/social-app +APP_NAME=Aiat +PKG=aiat +TEAM_NAME= +TEAM_ID= +CERT="Apple Distribution: ${TEAM_NAME} (${TEAM_ID})" +MAIL=user@example.com +KEY_CHAIN=EXAMPLE + +cd $d +# npx expo prebuild --clean +# cd ios && pod install && cd .. + +## アーカイブ +xcodebuild -workspace ios/${PKG}.xcworkspace \ + -scheme ${PKG} \ + -configuration Release \ + -archivePath build/${APP_NAME}.xcarchive \ + -allowProvisioningUpdates \ + archive + +cd build + +# IPA作成 +rm -rf Payload ${APP_NAME}.ipa +mkdir -p Payload +cp -R ${APP_NAME}.xcarchive/Products/Applications/${PKG}.app Payload/ +cp ../store.mobileprovision Payload/${PKG}.app/embedded.mobileprovision + +# entitlements抽出 +security cms -D -i Payload/${PKG}.app/embedded.mobileprovision > /tmp/profile.plist +/usr/libexec/PlistBuddy -x -c "Print :Entitlements" /tmp/profile.plist > /tmp/entitlements.plist + +codesign -f -s "$CERT" Payload/${PKG}.app/Frameworks/*.framework 2>/dev/null || true +codesign -f -s "$CERT" --entitlements /tmp/entitlements.plist Payload/${PKG}.app + +zip -r ${APP_NAME}.ipa Payload + +xcrun altool --upload-app -f ${APP_NAME}.ipa -t ios -u "${MAIL}" -p "@keychain:${KEY_CHAIN}" + +echo "Upload complete" diff --git a/social-app-custom/bin/install.zsh b/social-app-custom/bin/install.zsh new file mode 100644 index 0000000..2e372c7 --- /dev/null +++ b/social-app-custom/bin/install.zsh @@ -0,0 +1,86 @@ +#!/bin/zsh + +if [ "$1" = "social-app-custom" ];then + at-social-app-custom-pages + at-social-app-custom-screens + at-social-app-aiat-config + at-social-app-aiat-logo + at-origin-social-app + exit +fi + +function at-social-app-custom-pages() { + d_=$d/repos/social-app + custom=$d/social-app-custom + + echo "copying custom components to social-app" + + # Create components directory if not exists + mkdir -p ${d_}/src/components/custom + + # Copy custom components + cp ${custom}/PrivacyContent.tsx ${d_}/src/components/custom/ + cp ${custom}/AppInfo.tsx ${d_}/src/components/custom/ + + echo "custom components copied successfully" +} + +function at-social-app-aiat-config() { + d_=$d/repos/social-app + custom=$d/social-app-custom + + echo "applying Aiat configuration" + + # Update app.config.js + cd ${d_} + + # Backup original + cp app.config.js app.config.js.orig + + # Apply changes using sed + sed -i "s/name: 'Bluesky'/name: 'Aiat'/g" app.config.js + sed -i "s/slug: 'bluesky'/slug: 'aiat'/g" app.config.js + sed -i "s/scheme: 'bluesky'/scheme: 'aiat'/g" app.config.js + sed -i "s/owner: 'blueskysocial'/owner: 'syui'/g" app.config.js + sed -i "s/bundleIdentifier: 'xyz.blueskyweb.app'/bundleIdentifier: 'ai.syui.at'/g" app.config.js + + # Update package.json name + sed -i 's/"name": "bsky.app"/"name": "aiat"/g' package.json + + echo "Aiat configuration applied" +} + +function at-social-app-aiat-logo() { + d_=$d/repos/social-app + custom=$d/social-app-custom + + echo "applying Aiat logo" + + # Create logo directory if not exists + mkdir -p ${custom}/assets + + # Copy logo if exists in custom folder + if [ -f ${custom}/assets/icon.png ]; then + cp ${custom}/assets/icon.png ${d_}/assets/app-icons/ios_icon_default_next.png + echo "Aiat logo applied" + else + echo "Warning: Logo file not found at ${custom}/assets/icon.png" + echo "Please add your logo file there" + fi +} + +function at-social-app-custom-screens() { + d_=$d/repos/social-app + custom=$d/social-app-custom + + echo "applying custom screens" + + # Copy custom screen files + cp ${custom}/PrivacyPolicy.screen.tsx ${d_}/src/view/screens/PrivacyPolicy.tsx + cp ${custom}/Support.screen.tsx ${d_}/src/view/screens/Support.tsx + cp ${custom}/LicenseNotice.tsx ${d_}/src/components/custom/ + + echo "custom screens applied" +} + +