annot.at is a service for syncing static websites, such as blogs, to atproto and standard.site annot.at
elixir atproto standardsite
3

Configure Feed

Select the types of activity you want to include in your feed.

Sensitive fields should be encrypted

Johanna Larsson (Jun 15, 2026, 10:14 PM +0100) ec889b62 7888282f

+39 -60
+7
config/config.exs
··· 65 65 signing_jwk: 66 66 ~s({"crv":"P-256","d":"h0MvqcXLcKqWZFnqUCAuc6Bmt6gGzj5F5sFOCaUD4Jw","kty":"EC","x":"u0_K5EPDBIlGVp_rUUKucDviS-Owhiv4jnpMCeI7ojY","y":"v_XGrdUIww1wsRA7TUqMWIAJXmi2V8mnoF24Vg5OkvQ"}) 67 67 68 + config :annot_at, AnnotAt.Vault, 69 + ciphers: [ 70 + default: 71 + {Cloak.Ciphers.AES.GCM, 72 + tag: "AES.GCM.V1", key: Base.decode64!("8h8cn7yiPKyVKiCURGpw7/lUPqxEOahH2C+0CdGQIbI=")} 73 + ] 74 + 68 75 # Import environment specific config. This must remain at the bottom 69 76 # of this file so it overrides the configuration defined above. 70 77 import_config "#{config_env()}.exs"
+6 -49
config/runtime.exs
··· 71 71 ], 72 72 secret_key_base: secret_key_base 73 73 74 - # ## SSL Support 75 - # 76 - # To get SSL working, you will need to add the `https` key 77 - # to your endpoint configuration: 78 - # 79 - # config :annot_at, AnnotAtWeb.Endpoint, 80 - # https: [ 81 - # ..., 82 - # port: 443, 83 - # cipher_suite: :strong, 84 - # keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"), 85 - # certfile: System.get_env("SOME_APP_SSL_CERT_PATH") 86 - # ] 87 - # 88 - # The `cipher_suite` is set to `:strong` to support only the 89 - # latest and more secure SSL ciphers. This means old browsers 90 - # and clients may not be supported. You can set it to 91 - # `:compatible` for wider support. 92 - # 93 - # `:keyfile` and `:certfile` expect an absolute path to the key 94 - # and cert in disk or a relative path inside priv, for example 95 - # "priv/ssl/server.key". For all supported SSL configuration 96 - # options, see https://hexdocs.pm/plug/Plug.SSL.html#configure/1 97 - # 98 - # We also recommend setting `force_ssl` in your config/prod.exs, 99 - # ensuring no data is ever sent via http, always redirecting to https: 100 - # 101 - # config :annot_at, AnnotAtWeb.Endpoint, 102 - # force_ssl: [hsts: true] 103 - # 104 - # Check `Plug.SSL` for all available options in `force_ssl`. 105 - 106 - # ## Configuring the mailer 107 - # 108 - # In production you need to configure the mailer to use a different adapter. 109 - # Here is an example configuration for Mailgun: 110 - # 111 - # config :annot_at, AnnotAt.Mailer, 112 - # adapter: Swoosh.Adapters.Mailgun, 113 - # api_key: System.get_env("MAILGUN_API_KEY"), 114 - # domain: System.get_env("MAILGUN_DOMAIN") 115 - # 116 - # Most non-SMTP adapters require an API client. Swoosh supports Req, Hackney, 117 - # and Finch out-of-the-box. This configuration is typically done at 118 - # compile-time in your config/prod.exs: 119 - # 120 - # config :swoosh, :api_client, Swoosh.ApiClient.Req 121 - # 122 - # See https://hexdocs.pm/swoosh/Swoosh.html#module-installation for details. 74 + config :annot_at, AnnotAt.Vault, 75 + ciphers: [ 76 + default: 77 + {Cloak.Ciphers.AES.GCM, 78 + tag: "AES.GCM.V1", key: Base.decode64!(System.fetch_env!("CLOAK_KEY"))} 79 + ] 123 80 end
+3 -3
lib/annot_at/accounts/atproto_session.ex
··· 8 8 field :auth_server_issuer, :string 9 9 # Space-separated scopes from token response 10 10 field :granted_scopes, :string 11 - field :access_token, :string 12 - field :refresh_token, :string 11 + field :access_token, AnnotAt.Encrypted.Binary 12 + field :refresh_token, AnnotAt.Encrypted.Binary 13 13 # ES256 keypair for this session 14 - field :dpop_private_jwk, :string 14 + field :dpop_private_jwk, AnnotAt.Encrypted.Binary 15 15 # Access token expiry 16 16 field :expires_at, :utc_datetime 17 17
+2 -2
lib/annot_at/accounts/oauth_login_request.ex
··· 11 11 field :pds_host, :string 12 12 field :auth_server_issuer, :string 13 13 # PKCE verifier, its challenge went out in PAR, the verifier redeems the code 14 - field :pkce_verifier, :string 14 + field :pkce_verifier, AnnotAt.Encrypted.Binary 15 15 # Per-session DPoP key (serialized), tokens get bound to it at exchange 16 - field :dpop_private_jwk, :string 16 + field :dpop_private_jwk, AnnotAt.Encrypted.Binary 17 17 18 18 timestamps(type: :utc_datetime) 19 19 end
+1
lib/annot_at/application.ex
··· 9 9 def start(_type, _args) do 10 10 children = [ 11 11 AnnotAtWeb.Telemetry, 12 + AnnotAt.Vault, 12 13 AnnotAt.Repo, 13 14 {DNSCluster, query: Application.get_env(:annot_at, :dns_cluster_query) || :ignore}, 14 15 {Phoenix.PubSub, name: AnnotAt.PubSub},
+5
lib/annot_at/encrypted/binary.ex
··· 1 + defmodule AnnotAt.Encrypted.Binary do 2 + @moduledoc false 3 + 4 + use Cloak.Ecto.Binary, vault: AnnotAt.Vault 5 + end
+5
lib/annot_at/vault.ex
··· 1 + defmodule AnnotAt.Vault do 2 + @moduledoc false 3 + 4 + use Cloak.Vault, otp_app: :annot_at 5 + end
+3 -1
mix.exs
··· 68 68 {:bandit, "~> 1.5"}, 69 69 {:jose, "~> 1.11"}, 70 70 {:credo, "~> 1.7", only: [:dev, :test]}, 71 - {:mimic, "~> 2.3", only: :test} 71 + {:mimic, "~> 2.3", only: :test}, 72 + {:cloak, "~> 1.1"}, 73 + {:cloak_ecto, "~> 1.3"} 72 74 ] 73 75 end 74 76
+2
mix.lock
··· 2 2 "bandit": {:hex, :bandit, "1.12.0", "6c5214daa2469644ac4ab0113b98abc24f75e348378e6a974c6343b3e5da22ef", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.18", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.5", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "45dac82dc86f45cf4a196dee9cc5a8b791d9c9469d996055f055e6ee36c66e20"}, 3 3 "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, 4 4 "cc_precompiler": {:hex, :cc_precompiler, "0.1.11", "8c844d0b9fb98a3edea067f94f616b3f6b29b959b6b3bf25fee94ffe34364768", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3427232caf0835f94680e5bcf082408a70b48ad68a5f5c0b02a3bea9f3a075b9"}, 5 + "cloak": {:hex, :cloak, "1.1.4", "aba387b22ea4d80d92d38ab1890cc528b06e0e7ef2a4581d71c3fdad59e997e7", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "92b20527b9aba3d939fab0dd32ce592ff86361547cfdc87d74edce6f980eb3d7"}, 6 + "cloak_ecto": {:hex, :cloak_ecto, "1.3.0", "0de127c857d7452ba3c3367f53fb814b0410ff9c680a8d20fbe8b9a3c57a1118", [:mix], [{:cloak, "~> 1.1.1", [hex: :cloak, repo: "hexpm", optional: false]}, {:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}], "hexpm", "314beb0c123b8a800418ca1d51065b27ba3b15f085977e65c0f7b2adab2de1cc"}, 5 7 "credo": {:hex, :credo, "1.7.19", "cc52129665fc7c15143d47838fda0f9cd6dac9ceced7bf4da6f85fcbfe64b12a", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "2d8bc95d5a7bb99dd2613621d4f08c6a3575c3fd4b62e6a2b48a100352a557b8"}, 6 8 "db_connection": {:hex, :db_connection, "2.10.1", "d5465f6bcc125c1b8981c1dbf23c193ca16f446ec0b25832dc174f74f18be510", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "18ed94c6e627b4bf452dbd4df61b69a35a1e768525140bc1917b7a685026a6a3"}, 7 9 "decimal": {:hex, :decimal, "3.1.1", "430d87b04011ce6cbd4fd205be758311a81f87d552d40904abd00f015935b1d0", [:mix], [], "hexpm", "c5f25f2ced74a0587d03e6023f595db8e924c9d3922c8c8ffd9edfc4498cf1f6"},
+3 -3
priv/repo/migrations/20260608073605_atproto_sessions.exs
··· 6 6 add :user_id, references(:users, on_delete: :delete_all), null: false 7 7 add :auth_server_issuer, :text, null: false 8 8 add :granted_scopes, :text, null: false 9 - add :access_token, :text, null: false 10 - add :refresh_token, :text, null: false 11 - add :dpop_private_jwk, :text, null: false 9 + add :access_token, :binary, null: false 10 + add :refresh_token, :binary, null: false 11 + add :dpop_private_jwk, :binary, null: false 12 12 add :expires_at, :utc_datetime, null: false 13 13 14 14 timestamps(type: :utc_datetime)
+2 -2
priv/repo/migrations/20260615144354_create_oauth_login_requests.exs
··· 8 8 add :handle, :text, null: false 9 9 add :pds_host, :text, null: false 10 10 add :auth_server_issuer, :text, null: false 11 - add :pkce_verifier, :text, null: false 12 - add :dpop_private_jwk, :text, null: false 11 + add :pkce_verifier, :binary, null: false 12 + add :dpop_private_jwk, :binary, null: false 13 13 14 14 timestamps(type: :utc_datetime) 15 15 end