1
0
hugo/content/blog/2024-01-08-bluesky.md

598 lines
24 KiB
Markdown
Raw Permalink Normal View History

2024-04-23 13:21:26 +00:00
+++
date = "2024-01-08"
tags = ["bluesky","pds"]
title = "blueskyをself-hostする"
slug = "bluesky"
+++
少し前にblueskyをself-host(セルフホスト)しました。
![](https://raw.githubusercontent.com/syui/img/master/other/bluesky_atproto_self_hosting_20240108_0001.png)
- https://web.syu.is
|server|url|src|
|---|---|---|
|pds|https://syu.is|[src](https://github.com/bluesky-social/atproto/tree/main/services/pds)|
|plc|https://plc.syu.is|[src](https://github.com/did-method-plc/did-method-plc/tree/main/packages/server)|
|mod|https://mod.syu.is|[src](https://github.com/bluesky-social/atproto/tree/main/services/ozone)|
|bgs|https://bgs.syu.is|[src](https://github.com/bluesky-social/indigo/tree/main/cmd/bigsky)|
|appview|https://api.syu.is|[src](https://github.com/bluesky-social/atproto/tree/main/services/bsky)|
|web|https://web.syu.is|[src](https://github.com/bluesky-social/social-app)|
以前はbluesky(pds+appview)を動かしていたのですが、一般的にblueskyと呼ばれるものは複数のserverに依存しています。例えば、plc, bgsです。今ではpdsにあったappviewも分離しています。
bsky.teamがそれぞれのsandboxを用意してくれていますが、全部をself-hostしないといつか動かなくなります。
|server|env|body|
|---|---|---|
|pds|PDS_DID_PLC_URL|`https://plc.${host}`|
|pds|PDS_BSKY_APP_VIEW_URL|`https://api.${host}`|
|pds|PDS_BSKY_APP_VIEW_DID|did:web:api.${host}|
|pds|PDS_MOD_SERVICE_URL|`https://mod.${host}`|
|pds|PDS_MOD_SERVICE_DID|did:web:mod.${host}|
|pds|PDS_CRAWLERS|`https://bgs.${host}`|
nostrで活動されているikuradonさんが全部のserverをセルフホストしているのを見て、どうやら他のserverも動作できる環境にあるようだと思ったので立ててみました。なお、ikuradonさんに結構助けてもらった。
今回、`syu.is`というdomainを取りました。なお、`.is`はあまりおすすめしません。これはアイスランドが独自方針で管理している感じで、メール認証などが必要です。その他、様々な制限があります。
```sh
git clone https://github.com/bluesky-social/atproto
cd atproto
git clone https://github.com/did-method-plc/did-method-plc ./repos/did-method-plc
git clone https://github.com/bluesky-social/indigo ./repos/indigo
git clone https://github.com/bluesky-social/social-app ./repos/social-app
touch .plc.env .bsky.env .bgs.env .pds.env .db.env .mod.env .web.env
```
```sh
mkdir -p ./postgres/init
echo '-- plc
CREATE DATABASE plc;
GRANT ALL PRIVILEGES ON DATABASE plc TO postgres;
-- bgs
CREATE DATABASE bgs;
CREATE DATABASE carstore;
GRANT ALL PRIVILEGES ON DATABASE bgs TO postgres;
GRANT ALL PRIVILEGES ON DATABASE carstore TO postgres;
-- bsky(appview)
CREATE DATABASE bsky;
GRANT ALL PRIVILEGES ON DATABASE bsky TO postgres;
-- ozone(moderation)
CREATE DATABASE mod;
GRANT ALL PRIVILEGES ON DATABASE mod TO postgres;
-- pds
CREATE DATABASE pds;
GRANT ALL PRIVILEGES ON DATABASE pds TO postgres;
' >> ./postgres/init/init.sql
```
### compose.yaml
docker composeで構築しました。大体は以下のような感じの構成です。
下記は最小構成なので、自分なりに読み替えてください。
```yml:compose.yaml
services:
plc:
ports:
- 2582:3000
build:
context: ./repos/did-method-plc/
dockerfile: packages/server/Dockerfile
env_file:
- ./.plc.env
depends_on:
- db
bgs:
ports:
- 2470:2470
build:
context: ./repos/indigo/
dockerfile: cmd/bigsky/Dockerfile
env_file:
- ./.bgs.env
volumes:
- ./data/bgs/:/data/
depends_on:
- db
bsky:
ports:
- 2584:3000
build:
context: ./
dockerfile: services/bsky/Dockerfile
env_file:
- ./.bsky.env
depends_on:
- db
- redis
bsky-daemon:
build:
context: ./
dockerfile: services/bsky/Dockerfile
command: node --enable-source-maps daemon.js
env_file:
- ./.bsky.env
depends_on:
- bsky
- db
- redis
bsky-indexer:
build:
context: ./
dockerfile: services/bsky/Dockerfile
command: node --enable-source-maps indexer.js
env_file:
- ./.bsky.env
volumes:
- ./data/bsky/cache/:/cache/
depends_on:
- bsky
- db
- redis
bsky-ingester:
build:
context: ./
dockerfile: services/bsky/Dockerfile
command: node --enable-source-maps ingester.js
env_file:
- ./.bsky.env
volumes:
- ./data/bsky/cache/:/cache/
depends_on:
- bsky
- db
- redis
mod:
ports:
- 2585:3000
build:
context: ./
dockerfile: services/ozone/Dockerfile
env_file:
- ./.mod.env
depends_on:
- db
mod-daemon:
build:
context: ./
dockerfile: services/ozone/Dockerfile
command: node --enable-source-maps daemon.js
env_file:
- ./.mod.env
depends_on:
- mod
- db
pds:
ports:
- 2583:3000
build:
context: ./
dockerfile: services/pds/Dockerfile
env_file:
- ./.pds.env
volumes:
- ./data/pds/:/data/
depends_on:
- db
social-app:
ports:
- 8100:8100
build:
context: ./repos/social-app/
dockerfile: Dockerfile
env_file:
- ./.web.env
command: "/usr/bin/bskyweb serve"
db:
image: postgres:latest
env_file:
- ./.db.env
volumes:
- ./postgres/init/:/docker-entrypoint-initdb.d/
- ./data/db/:/var/lib/postgresql/data/
redis:
image: redis
volumes:
- ./data/redis/:/data/
```
### hint
必要に応じて、以下の設定などを使用するとよいでしょう。
```yaml:compose.yaml
services:
plc:
depends_on:
db:
# 依存先のサービスが起動したら起動する
condition: service_started
```
```yaml:compose.yaml
services:
plc:
depends_on:
db:
# 依存先のサービスが起動して、なおかつ、 healthcheck が通ったら起動する
condition: service_healthy
db:
healthcheck:
# https://docs.docker.jp/compose/compose-file/compose-file-v3.html
test: ["CMD-SHELL", "pg_isready"]
interval: 10s
timeout: 5s
retries: 5
```
```yaml:compose.yaml
services:
plc:
# https://docs.docker.jp/v19.03/config/container/start-containers-automatically.html
# コンテナが停止すると常に再起動します
restart: always
```
```yaml:compose.yaml
services:
db:
# https://hub.docker.com/_/postgres
# postgresのversionを固定
image: postgres:16
```
```yaml:compose.yaml
services:
db:
# localhostからのアクセスを可能にする
# postgresql://postgres:postgres@127.0.0.1:5432
ports:
- 5432:5432
```
```yaml:compose.yaml
services:
db:
# postgresql://user:password@127.0.0.1/test
environment:
POSTGRES_USER: user
POSTGRES_DB: test
POSTGRES_PASSWORD: password
```
### env
dbのurlになります。全部別々の`.env`に書いてください。
```sh:.*.env
# pds
PDS_DB_POSTGRES_URL=postgresql://postgres:postgres@db/pds
# bsky(appview)
DB_PRIMARY_POSTGRES_URL=postgres://postgres:postgres@db/bsky
DB_REPLICA_POSTGRES_URLS=postgres://postgres:postgres@db/bsky
# bgs
DATABASE_URL=postgres://postgres:postgres@db/bgs
CARSTORE_DATABASE_URL=postgres://postgres:postgres@db/carstore
# mod
OZONE_DB_POSTGRES_URL=postgres://postgres:postgres@db/mod
# plc
DATABASE_URL=postgres://postgres:postgres@db/plc
# email
PDS_EMAIL_SMTP_URL=smtps://$username:$password@smtp.gmail.com
```
環境変数をまとめます。
|server|env|body|
|---|---|---|
|bsky|DB_PRIMARY_POSTGRES_URL|postgres://postgres:postgres@db/bsky|
|bsky|DB_REPLICA_POSTGRES_URLS|postgres://postgres:postgres@db/bsky|
|bsky|DB_REPLICA_TAGS_ANY|0|
|bsky|PUBLIC_URL|`https://api.${host}`|
|bsky|SERVER_DID|did:web:api.${host}|
|bsky|DID_PLC_URL|`https://plc.${host}`|
|bsky|BLOB_CACHE_LOC|/cache/|
|bsky|SEARCH_ENDPOINT|`https://search.${host}`|
|bsky|REDIS_HOST|redis|
|bsky|INDEXER_PARTITION_IDS|0|
|bsky|INGESTER_PARTITION_COUNT|1|
|bsky|PUSH_NOTIFICATION_ENDPOINT|`https://push.bsky.${host}/api/push`|
|bsky|REPO_PROVIDER|wss://${host}|
|bsky|IMG_URI_ENDPOINT|`https://cdn.${host}/img`|
|bsky|ODERATION_SERVICE_DID|did:web:mod.${host}|
|bsky|MODERATION_PUSH_URL|`https://admin:${OZONE_ADMIN_PASSWORD}@mod.${host}`|
|bsky|ADMIN_PASSWORD|xxx|
|bsky|MODERATOR_PASSWORD|xxx|
|bsky|TRIAGE_PASSWORD|xxx|
|bsky|SERVICE_SIGNING_KEY|$ openssl ecparam --name secp256k1 --genkey ...|
|bsky|IMG_URI_SALT|xxx|
|bsky|IMG_URI_KEY|xxx|
|server|env|body|
|---|---|---|
|bgs|DATABASE_URL|postgres://postgres:postgres@db/bgs|
|bgs|CARSTORE_DATABASE_URL|postgres://postgres:postgres@db/carstore|
|bgs|DATA_DIR|/data|
|bgs|ATP_PLC_HOST|`https://plc.${host}`|
|bgs|BGS_ADMIN_KEY|xxx|
|server|env|body|
|---|---|---|
|mod|OZONE_PUBLIC_URL|`https://mod.${host}`|
|mod|OZONE_SERVER_DID|did:web:mod.${host}|
|mod|OZONE_APPVIEW_URL|`https://api.${host}`|
|mod|OZONE_APPVIEW_DID|did:web:api.${host}|
|mod|OZONE_PDS_URL|`https://${host}`|
|mod|OZONE_PDS_DID|did:web:${host}|
|mod|OZONE_DB_POSTGRES_URL|postgres://postgres:postgres@db/mod|
|mod|OZONE_DID_PLC_URL|`https://plc.${host}`|
|mod|MODERATION_PUSH_URL|`https://admin:${OZONE_ADMIN_PASSWORD}@mod.${host}`|
|mod|OZONE_ADMIN_PASSWORD|xxx|
|mod|OZONE_MODERATOR_PASSWORD|xxx|
|mod|OZONE_TRIAGE_PASSWORD|xxx|
|mod|OZONE_SIGNING_KEY_HEX|xxx|
|server|env|body|
|---|---|---|
|pds|PDS_HOSTNAME|${host}|
|pds|PDS_DATA_DIRECTORY|/data|
|pds|PDS_DB_POSTGRES_URL|postgresql://postgres:postgres@db/pds|
|pds|PDS_DID_PLC_URL|`https://plc.${host}`|
|pds|PDS_BSKY_APP_VIEW_URL|`https://api.${host}`|
|pds|PDS_BSKY_APP_VIEW_DID|did:web:api.${host}|
|pds|PDS_MOD_SERVICE_URL|`https://mod.${host}`|
|pds|PDS_MOD_SERVICE_DID|did:web:mod.${host}|
|pds|PDS_CRAWLERS|`https://bgs.${host}`|
|pds|PDS_EMAIL_SMTP_URL|smtps://$username:$password@smtp.gmail.com|
|pds|PDS_EMAIL_FROM_ADDRESS|no-reply@${host}|
|pds|PDS_INVITE_REQUIRED (招待コード)|false|
|pds|PDS_INVITE_INTERVAL|604800000|
|pds|PDS_BLOBSTORE_DISK_LOCATION|/data/img/static|
|pds|PDS_BLOBSTORE_DISK_TMP_LOCATION|/data/img/tmp|
|pds|PDS_JWT_SECRET|`$ openssl rand --hex 16`|
|pds|PDS_ADMIN_PASSWORD|xxx|
|pds|PDS_REPO_SIGNING_KEY_K256_PRIVATE_KEY_HEX|xxx|
|pds|PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX|xxx|
|server|env|body|
|---|---|---|
|plc|DATABASE_URL|postgres://postgres:postgres@db/plc|
|plc|DB_CREDS_JSON|'{"username":"postgres","password":"postgres","host":"db","port":"5432","database":"plc"}'|
|plc|ENABLE_MIGRATIONS|true|
|plc|DB_MIGRATE_CREDS_JSON|'{"username":"postgres","password":"postgres","host":"db","port":"5432","database":"plc"}'|
`xxx`は以下のコマンドなどで作成してもいいと思います。
```sh
$ openssl ecparam --name secp256k1 --genkey --noout --outform DER | tail --bytes=+8 | head --bytes=32 | xxd --plain --cols 32
```
### SERVER_DID / SERVICE_SIGNING_KEY
> `SERVICE_SIGNING_KEY`に入れる値は`SERVER_DID=did:web:xxx`の場合、ローカル環境で生成したもので構いません。しかし、`SERVER_DID=did:plc:xxx`を使用する場合はplcからkeyを登録します。
```sh
$ openssl ecparam --name secp256k1 --genkey --noout --outform DER | tail --bytes=+8 | head --bytes=32 | xxd --plain --cols 32
SERVICE_SIGNING_KEY=xxx
```
以下は現時点では必要ありません。
bsky(appview)の`SERVER_DID`は`did:web:xxx`という形式と`did:plc:xxx`という形式を使えます。後者はplcに登録して使うものと思われ、連合が開始する際に重要になるかもしれません。
`SERVICE_SIGNING_KEY`は`SERVER_DID`を取得したときのsign-keyだと思われます。
これはatprotoの`dev-env`でexampleが書かれています。
```ts:atproto/packages/dev-env/src/bsky.ts
static async create(cfg: BskyConfig): Promise<TestBsky> {
// packages/crypto/tests/keypairs.test.ts
const serviceKeypair = await Secp256k1Keypair.create({ exportable: true })
console.log(`ROTATION_KEY=${serviceKeypair.did()}`)
const exported = await serviceKeypair.export()
const plcClient = new PlcClient(cfg.plcUrl)
const port = cfg.port || (await getPort())
const url = `http://localhost:${port}`
const serverDid = await plcClient.createDid({
signingKey: serviceKeypair.did(),
rotationKeys: [serviceKeypair.did()],
handle: 'bsky.test',
pds: `http://localhost:${port}`,
signer: serviceKeypair,
})
console.log(`SERVER_DID=${serverDid}`)
const server = bsky.BskyAppView.create({
db,
redis: redisCache,
config,
algos: cfg.algos,
imgInvalidator: cfg.imgInvalidator,
signingKey: serviceKeypair,
})
```
```sh
$ make deps
$ make build
$ make test
$ make run-dev-env
```
appviewをcreateする際の`signingKey: serviceKeypair`の部分を見てください。objを使用しています。
つまり、signingKeyに`obj`を入れると動きますが、`services/bsky/api.ts`では以下のような処理がなされます。
```ts:atproto/services/bsky/api.js
const signingKey = await Secp256k1Keypair.import(env.serviceSigningKey)
```
didを作成したときに`Secp256k1Keypair`でimportできる値を`SERVICE_SIGNING_KEY`に入れてください。
あるいはコードを書き換えてobjをいれるのでもいけますが、現実的ではありません。
```ts:atproto/services/bsky/api.js
// const signingKey = await Secp256k1Keypair.import(env.serviceSigningKey)
const signingKey = process.env.SERVICE_SIGNING_OBJ
```
```ts:atproto/packages/crypto/tests/keypairs.test.ts
keypair = await Secp256k1Keypair.create({ exportable: true })
const exported = await keypair.export()
imported = await Secp256k1Keypair.import(exported, { exportable: true })
expect(keypair.did()).toBe(imported.did())
```
plcへの登録は以下のコマンドだと思われます。
```sh
# https://web.plc.directory/api/redoc#operation/ResolveDid
$ url=https://plc.$host/did:plc:pyc2ihzpelxtg4cdkfzbhcv4
$ json='{ "type": "create", "signingKey": "did:key:zQ3shP5TBe1sQfSttXty15FAEHV1DZgcxRZNxvEWnPfLFwLxJ", "recoveryKey": "did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg", "handle": "first-post.bsky.social", "service": "https://bsky.social", "prev": null, "sig": "yvN4nQYWTZTDl9nKSSyC5EC3nsF5g4S56OmRg9G6_-pM6FCItV2U2u14riiMGyHiCD86l6O-1xC5MPwf8vVsRw" }'
$ curl -X POST -H "Content-Type: application/json" -d "$json" $url | jq .
```
### invite-code
```sh
$ host=example.com
$ admin_password="admin-pass"
$ 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 | jq .
```
### social-app
svgを作りました。
<svg viewBox="0 0 2821.6379 794.29016" preserveAspectRatio="xMidYMid" xmlns:svg="http://www.w3.org/2000/svg"> <g transform="matrix(0.1,0,0,-0.1,-282.80153,1445)" fill="#000000" stroke="none" > <path d="m 24787,14443 c -4,-3 -7,-224 -7,-490 v -483 h 545 545 v 490 490 h -538 c -296,0 -542,-3 -545,-7 z" /> <path d="m 5190,13285 c -8,-3 -96,-12 -195,-20 -199,-16 -296,-32 -430,-70 -49,-14 -115,-32 -145,-40 -153,-39 -504,-198 -662,-301 -21,-13 -57,-36 -80,-51 -24,-16 -72,-50 -109,-78 -241,-182 -377,-315 -528,-517 -119,-158 -120,-160 -106,-188 23,-45 140,-140 560,-457 110,-84 319,-242 465,-353 146,-110 369,-279 495,-375 791,-600 723,-549 1049,-799 269,-207 398,-307 524,-403 l 114,-86 -49,-49 c -128,-131 -378,-258 -588,-299 -66,-13 -357,-13 -420,0 -115,23 -172,39 -202,54 -18,10 -37,17 -43,17 -24,0 -171,81 -255,141 -50,35 -146,121 -215,192 -69,70 -133,127 -142,127 -19,0 -153,-63 -177,-83 -9,-7 -54,-35 -101,-62 -47,-26 -110,-64 -140,-85 -30,-20 -97,-61 -148,-91 -51,-30 -107,-62 -124,-72 -18,-10 -57,-38 -87,-61 -70,-53 -252,-168 -446,-281 -82,-49 -158,-95 -168,-104 -16,-16 -14,-21 34,-106 165,-289 544,-666 867,-860 9,-6 35,-22 58,-37 36,-23 267,-138 349,-173 108,-47 160,-67 240,-93 150,-48 201,-62 228,-62 14,0 64,-9 109,-19 197,-46 302,-56 573,-56 197,1 281,5 345,17 47,9 110,19 141,22 31,3 75,13 99,21 23,8 56,15 72,15 17,0 51,7 77,15 25,8 80,24 121,36 41,12 125,41 185,66 61,25 124,50 140,56 17,7 89,42 160,78 113,58 177,98 395,246 82,56 273,232 403,371 127,136 237,271 237,291 0,12 -208,179 -425,342 -186,140 -1121,843 -1720,1294 -264,199 -589,444 -723,546 -134,101 -274,208 -312,237 -39,29 -70,58 -70,66 0,21 107,115 203,179 95,64 237,133 295,143 20,4 49,12 63,20 96,48 519,48 619,-1 14,-7 41,-16 60,-20 65,-13 262,-118 360,-191 99,-74 250,-230 372,-384 34,-44 70,-80 80,-80 9,0 39,15 67,33 28,17 70,44 94,58 23,15 121,76 217,136 96,60 254,156 350,213 96,56 272,160 390,230 118,70 230,135 248,144 41,21 41,45 -3,116 -18,30 -46,75 -61,100 -64,106 -252,348 -352,454 -106,112 -169,171 -293,274 -197,164 -310,235 -594,376 -215,106 -519,205 -735,240 -137,22 -574,51 -610,41 z" /> <path d="m 29095,12736 c -421,-44 -744,-157 -975,-342 -259,-207 -396,-446 -465,-809 -16,-84 -23,-301 -14,-425 36,-518 257,-859 694,-1070 166,-80 284,-116 716,-215 449,-103 514,-121 646,-186 218,-107 308,-253 306,-494 -2,-303 -188,-477 -588,-551 -140,-26 -640,-27 -825,-1 -275,38 -486,94 -776,203 -94,35 -174,64 -178,64 -3,0 -6,-175 -6,-389 v -388 l 88,-41 c 404,-187 905,-282 1486,-282 675,0 1154,150 1465,459 196,194 311,434 362,756 20,121 17,476 -5,600 -89,517 -358,800 -945,994 -130,43 -241,71 -616,156 -137,31 -299,73 -360,92 -331,106 -455,246 -455,511 1,249 127,412 387,501 272,92 801,76 1269,-39 116,-28 326,-96 432,-138 l 52,-22 -2,406 -3,406 -66,28 c -154,66 -413,140 -604,174 -279,49 -756,69 -1020,42 z" /> <path d="m 9630,12676 c 0,-2 403,-996 896,-2208 493,-1211 898,-2211 901,-2220 6,-21 -135,-386 -193,-499 -104,-202 -256,-324 -471,-380 -118,-30 -340,-43 -516,-29 -84,6 -185,17 -225,24 -40,7 -75,10 -77,7 -3,-2 -5,-163 -5,-357 v -353 l 63,-30 c 194,-93 493,-138 801,-120 414,23 683,115 937,319 174,140 357,402 474,680 26,62 1945,5159 1945,5167 0,2 -232,2 -516,1 l -517,-3 -556,-1550 c -485,-1350 -560,-1550 -578,-1553 -18,-3 -26,11 -62,105 -23,59 -295,758 -605,1553 l -562,1445 -567,3 c -312,1 -567,0 -567,-2 z" /> <path d="m 15690,10867 c 0,-1970 -1,-1936 56,-2153 128,-497 461,-787 1014,-885 147,-26 440,-36 605,-20 413,40 834,200 1181,447 35,25 73,44 88,44 14,0 26,-1 26,-3 0,-2 20,-94 45,-205 25,-111 45,-204 45,-207 0,-3 209,-5 465,-5 h 465 v 2400 2400 h -535 -535 l -2,-1866 -3,-1866 -115,-50 c -425,-185 -743,-252 -1088,-227 -302,21 -472,109 -562,293 -79,161 -74,11 -77,1969 l -3,1747 h -535 -535 z" /> <path d="m 24785,12668 c -3,-7 -4,-1086 -3,-2398 l 3,-2385 538,-3 537,-2 v 2400 2400 h -535 c -419,0 -537,-3 -540,-12 z" /> <path d="m 21660,8275 v -545 h 565 565 v 545 545 h -565 -565 z" /> </g> </svg>
```ts:social-app/src/view/icons/Logotype.tsx
import React from 'react'
import Svg, {Path, SvgProps, PathProps} from 'react-native-svg'
import {usePalette} from '#/lib/hooks/usePalette'
const ratio = 17 / 64
export function Logotype({
fill,
...rest
}: {fill?: PathProps['fill']} & SvgProps) {
const pal = usePalette('default')
// @ts-ignore it's fiiiiine
const size = parseInt(rest.width || 32)
return (
<Svg
fill="none"
viewBox="0 0 2821.6379 794.29016"
{...rest}
width={size}
height={Number(size) * ratio}>
<g
transform="matrix(0.1,0,0,-0.1,-282.80153,1445)"
fill="#000000"
stroke="none"
>
<path
d="m 24787,14443 c -4,-3 -7,-224 -7,-490 v -483 h 545 545 v 490 490 h -538 c -296,0 -542,-3 -545,-7 z"
/>
<path
d="m 5190,13285 c -8,-3 -96,-12 -195,-20 -199,-16 -296,-32 -430,-70 -49,-14 -115,-32 -145,-40 -153,-39 -504,-198 -662,-301 -21,-13 -57,-36 -80,-51 -24,-16 -72,-50 -109,-78 -241,-182 -377,-315 -528,-517 -119,-158 -120,-160 -106,-188 23,-45 140,-140 560,-457 110,-84 319,-242 465,-353 146,-110 369,-279 495,-375 791,-600 723,-549 1049,-799 269,-207 398,-307 524,-403 l 114,-86 -49,-49 c -128,-131 -378,-258 -588,-299 -66,-13 -357,-13 -420,0 -115,23 -172,39 -202,54 -18,10 -37,17 -43,17 -24,0 -171,81 -255,141 -50,35 -146,121 -215,192 -69,70 -133,127 -142,127 -19,0 -153,-63 -177,-83 -9,-7 -54,-35 -101,-62 -47,-26 -110,-64 -140,-85 -30,-20 -97,-61 -148,-91 -51,-30 -107,-62 -124,-72 -18,-10 -57,-38 -87,-61 -70,-53 -252,-168 -446,-281 -82,-49 -158,-95 -168,-104 -16,-16 -14,-21 34,-106 165,-289 544,-666 867,-860 9,-6 35,-22 58,-37 36,-23 267,-138 349,-173 108,-47 160,-67 240,-93 150,-48 201,-62 228,-62 14,0 64,-9 109,-19 197,-46 302,-56 573,-56 197,1 281,5 345,17 47,9 110,19 141,22 31,3 75,13 99,21 23,8 56,15 72,15 17,0 51,7 77,15 25,8 80,24 121,36 41,12 125,41 185,66 61,25 124,50 140,56 17,7 89,42 160,78 113,58 177,98 395,246 82,56 273,232 403,371 127,136 237,271 237,291 0,12 -208,179 -425,342 -186,140 -1121,843 -1720,1294 -264,199 -589,444 -723,546 -134,101 -274,208 -312,237 -39,29 -70,58 -70,66 0,21 107,115 203,179 95,64 237,133 295,143 20,4 49,12 63,20 96,48 519,48 619,-1 14,-7 41,-16 60,-20 65,-13 262,-118 360,-191 99,-74 250,-230 372,-384 34,-44 70,-80 80,-80 9,0 39,15 67,33 28,17 70,44 94,58 23,15 121,76 217,136 96,60 254,156 350,213 96,56 272,160 390,230 118,70 230,135 248,144 41,21 41,45 -3,116 -18,30 -46,75 -61,100 -64,106 -252,348 -352,454 -106,112 -169,171 -293,274 -197,164 -310,235 -594,376 -215,106 -519,205 -735,240 -137,22 -574,51 -610,41 z"
/>
<path
d="m 29095,12736 c -421,-44 -744,-157 -975,-342 -259,-207 -396,-446 -465,-809 -16,-84 -23,-301 -14,-425 36,-518 257,-859 694,-1070 166,-80 284,-116 716,-215 449,-103 514,-121 646,-186 218,-107 308,-253 306,-494 -2,-303 -188,-477 -588,-551 -140,-26 -640,-27 -825,-1 -275,38 -486,94 -776,203 -94,35 -174,64 -178,64 -3,0 -6,-175 -6,-389 v -388 l 88,-41 c 404,-187 905,-282 1486,-282 675,0 1154,150 1465,459 196,194 311,434 362,756 20,121 17,476 -5,600 -89,517 -358,800 -945,994 -130,43 -241,71 -616,156 -137,31 -299,73 -360,92 -331,106 -455,246 -455,511 1,249 127,412 387,501 272,92 801,76 1269,-39 116,-28 326,-96 432,-138 l 52,-22 -2,406 -3,406 -66,28 c -154,66 -413,140 -604,174 -279,49 -756,69 -1020,42 z"
/>
<path
d="m 9630,12676 c 0,-2 403,-996 896,-2208 493,-1211 898,-2211 901,-2220 6,-21 -135,-386 -193,-499 -104,-202 -256,-324 -471,-380 -118,-30 -340,-43 -516,-29 -84,6 -185,17 -225,24 -40,7 -75,10 -77,7 -3,-2 -5,-163 -5,-357 v -353 l 63,-30 c 194,-93 493,-138 801,-120 414,23 683,115 937,319 174,140 357,402 474,680 26,62 1945,5159 1945,5167 0,2 -232,2 -516,1 l -517,-3 -556,-1550 c -485,-1350 -560,-1550 -578,-1553 -18,-3 -26,11 -62,105 -23,59 -295,758 -605,1553 l -562,1445 -567,3 c -312,1 -567,0 -567,-2 z"
/>
<path
d="m 15690,10867 c 0,-1970 -1,-1936 56,-2153 128,-497 461,-787 1014,-885 147,-26 440,-36 605,-20 413,40 834,200 1181,447 35,25 73,44 88,44 14,0 26,-1 26,-3 0,-2 20,-94 45,-205 25,-111 45,-204 45,-207 0,-3 209,-5 465,-5 h 465 v 2400 2400 h -535 -535 l -2,-1866 -3,-1866 -115,-50 c -425,-185 -743,-252 -1088,-227 -302,21 -472,109 -562,293 -79,161 -74,11 -77,1969 l -3,1747 h -535 -535 z"
/>
<path
d="m 24785,12668 c -3,-7 -4,-1086 -3,-2398 l 3,-2385 538,-3 537,-2 v 2400 2400 h -535 c -419,0 -537,-3 -540,-12 z"
/>
<path
d="m 21660,8275 v -545 h 565 565 v 545 545 h -565 -565 z"
/>
</g>
</Svg>
)
}
```
その他、書き換えを行うscriptです。頻繁にupdateすると思うので、mergeはきつい。
```sh
host=syu.is
name=${host%%.*}
domain=${host##*.}
cd $d/repos/social-app/src
if [ -n "`grep -R bsky.social .`" ];then
for f (`grep -R bsky.social . |cut -d : -f 1`) sed -i -e "s/bsky\.social/${name}\.${domain}/g" $f
fi
if [ -n "`grep -R "isSandbox: false" .`" ];then
for f (`grep -R "isSandbox: false" . |cut -d : -f 1`) sed -i -e "s/isSandbox: false/isSandbox: true/g" $f
fi
if [ -n "`grep -R SANDBOX .`" ];then
for f (`grep -R SANDBOX . |cut -d : -f 1`) sed -i -e "s/SANDBOX/${name}\.${domain}/g" $f
fi
f=./view/com/modals/ServerInput.tsx
if [ -n "`grep -R Bluesky.Social $f`" ] && [ -f $f ];then
sed -i -e "s/Bluesky\.Social/${name}\.${domain}/g" $f
fi
f=./state/queries/preferences/moderation.ts
if [ -n "`grep -R 'Bluesky Social' $f`" ] && [ -f $f ];then
sed -i -e "s/Bluesky Social/${name}\.${domain}/g" $f
fi
f=./view/com/auth/create/Step1.tsx
if [ -n "`grep -R 'Bluesky' $f`" ] && [ -f $f ];then
sed -i -e "s/Bluesky/${name}\.${domain}/g" $f
fi
f=./lib/strings/url-helpers.ts
if [ -n "`grep -R 'Bluesky Social' $f`" ] && [ -f $f ];then
sed -i -e "s/Bluesky Social/${name}\.${domain}/g" $f
fi
f=./view/icons/Logotype.tsx
o=$d/icons/Logotype.tsx
if [ -n "`grep -R 'M8.478 6.252c1.503.538 2.3 1.7' $f`" ] && [ -f $f ] && [ -f $o ];then
cp -rf $o $f
fi
```