···11# Airport
2233-Your terminal for seamless AT Protocol PDS (Personal Data Server) migration and backup.
33+Your terminal for seamless AT Protocol PDS (Personal Data Server) migration and
44+backup.
4555-Airport is a web application built with Fresh and Deno that helps users safely migrate and backup their Bluesky PDS data. It provides a user-friendly interface for managing your AT Protocol data.
66+Airport is a web application built with Fresh and Deno that helps users safely
77+migrate and backup their Bluesky PDS data. It provides a user-friendly interface
88+for managing your AT Protocol data.
69710## Features
811···30333134## About
32353333-Airport is developed with ❤️ by [Roscoe](https://bsky.app/profile/knotbin.com) for [Spark](https://sprk.so), a new short-video platform for AT Protocol.
3636+Airport is developed with ❤️ by [Roscoe](https://bsky.app/profile/knotbin.com)
3737+for [Spark](https://sprk.so), a new short-video platform for AT Protocol.
34383539## Contributing
36403737-We welcome contributions! Please feel free to submit a Pull Request. Please only submit pull requests that are relevant to the project. This project targets people with a non-advanced understanding of AT Protocol, so please avoid submitting pull requests that add features that complicate the user experience.
4141+We welcome contributions! Please feel free to submit a Pull Request. Please only
4242+submit pull requests that are relevant to the project. This project targets
4343+people with a non-advanced understanding of AT Protocol, so please avoid
4444+submitting pull requests that add features that complicate the user experience.
38453946## License
4047
···1515 * @returns The migration state information
1616 */
1717export function getMigrationState(): MigrationStateInfo {
1818- const state = (Deno.env.get("MIGRATION_STATE") || "up").toLowerCase() as MigrationState;
1818+ const state = (Deno.env.get("MIGRATION_STATE") || "up")
1919+ .toLowerCase() as MigrationState;
19202021 switch (state) {
2122 case "issue":
2223 return {
2324 state: "issue",
2424- message: "Migration services are temporarily unavailable as we investigate an issue. Please try again later.",
2525+ message:
2626+ "Migration services are temporarily unavailable as we investigate an issue. Please try again later.",
2527 allowMigration: false,
2628 };
27292830 case "maintenance":
2931 return {
3032 state: "maintenance",
3131- message: "Migration services are temporarily unavailable for maintenance. Please try again later.",
3333+ message:
3434+ "Migration services are temporarily unavailable for maintenance. Please try again later.",
3235 allowMigration: false,
3336 };
3437
+2-2
lib/oauth/sessions.ts
···11import { Agent } from "npm:@atproto/api";
22import { getIronSession, SessionOptions } from "npm:iron-session";
33import { oauthClient } from "./client.ts";
44-import { OauthSession, createSessionOptions } from "../types.ts";
44+import { createSessionOptions, OauthSession } from "../types.ts";
5566let oauthSessionOptions: SessionOptions;
77···2222 * @returns The OAuth session agent
2323 */
2424export async function getOauthSessionAgent(
2525- req: Request
2525+ req: Request,
2626) {
2727 try {
2828 console.log("Getting OAuth session...");
+32-26
lib/types.ts
···1515 * @param db - The Deno KV instance for the database
1616 * @returns The unlock function
1717 */
1818-async function createLock(key: string, db: Deno.Kv): Promise<() => Promise<void>> {
1818+async function createLock(
1919+ key: string,
2020+ db: Deno.Kv,
2121+): Promise<() => Promise<void>> {
1922 const lockKey = ["session_lock", key];
2023 const lockValue = Date.now();
2121-2424+2225 // Try to acquire lock
2326 const result = await db.atomic()
2424- .check({ key: lockKey, versionstamp: null }) // Only if key doesn't exist
2525- .set(lockKey, lockValue, { expireIn: 5000 }) // 5 second TTL
2727+ .check({ key: lockKey, versionstamp: null }) // Only if key doesn't exist
2828+ .set(lockKey, lockValue, { expireIn: 5000 }) // 5 second TTL
2629 .commit();
27302831 if (!result.ok) {
···4043 * @type {OauthSession}
4144 */
4245export interface OauthSession {
4343- did: string
4646+ did: string;
4447}
45484649/**
···6871 * @param cookieName - The name of the iron session cookie
6972 * @returns The session options for iron session
7073 */
7171-export const createSessionOptions = async (cookieName: string): Promise<SessionOptions> => {
7272- const cookieSecret = Deno.env.get("COOKIE_SECRET");
7373- if (!cookieSecret) {
7474- throw new Error("COOKIE_SECRET is not set");
7575- }
7474+export const createSessionOptions = async (
7575+ cookieName: string,
7676+): Promise<SessionOptions> => {
7777+ const cookieSecret = Deno.env.get("COOKIE_SECRET");
7878+ if (!cookieSecret) {
7979+ throw new Error("COOKIE_SECRET is not set");
8080+ }
76817777- if (!db) {
7878- db = await Deno.openKv();
7979- }
8282+ if (!db) {
8383+ db = await Deno.openKv();
8484+ }
80858181- return {
8282- cookieName: cookieName,
8383- password: cookieSecret,
8484- cookieOptions: {
8585- secure: Deno.env.get("NODE_ENV") === "production" || Deno.env.get("NODE_ENV") === "staging",
8686- httpOnly: true,
8787- sameSite: "lax",
8888- path: "/",
8989- domain: undefined,
9090- },
9191- lockFn: (key: string) => createLock(key, db)
9292- }
9393-};8686+ return {
8787+ cookieName: cookieName,
8888+ password: cookieSecret,
8989+ cookieOptions: {
9090+ secure: Deno.env.get("NODE_ENV") === "production" ||
9191+ Deno.env.get("NODE_ENV") === "staging",
9292+ httpOnly: true,
9393+ sameSite: "lax",
9494+ path: "/",
9595+ domain: undefined,
9696+ },
9797+ lockFn: (key: string) => createLock(key, db),
9898+ };
9999+};
+5-5
routes/_error.tsx
···11-import { PageProps, HttpError } from "fresh";
11+import { HttpError, PageProps } from "fresh";
22import posthog from "posthog-js";
3344export default function ErrorPage(props: PageProps) {
55 const error = props.error; // Contains the thrown Error or HTTPError
66 if (error instanceof HttpError) {
77- posthog.default.capture('error', {
77+ posthog.default.capture("error", {
88 error: error.message,
99 status: error.status,
1010 });
···3232 FLIGHT NOT FOUND
3333 </p>
3434 <p class="text-lg sm:text-xl text-slate-600 dark:text-white/70 max-w-2xl">
3535- We couldn't locate the destination you're looking for. Please
3636- check your flight number and try again.
3535+ We couldn't locate the destination you're looking for.
3636+ Please check your flight number and try again.
3737 </p>
3838 <div class="mt-8">
3939 <a
···4848 </div>
4949 </div>
5050 </>
5151- )
5151+ );
5252 }
5353 }
5454
+68-38
routes/about.tsx
···66 <div class="px-2 sm:px-4 py-4 sm:py-8 mx-auto">
77 <div class="max-w-screen-lg mx-auto flex flex-col items-center justify-center">
88 <div class="prose dark:prose-invert max-w-none w-full mb-0">
99- <h1 class="text-3xl font-bold text-center mb-8">About AT Protocol</h1>
99+ <h1 class="text-3xl font-bold text-center mb-8">
1010+ About AT Protocol
1111+ </h1>
10121113 <div class="space-y-6">
1214 <section>
1313- <h2 class="text-2xl font-semibold mb-4">What is AT Protocol?</h2>
1515+ <h2 class="text-2xl font-semibold mb-4">
1616+ What is AT Protocol?
1717+ </h2>
1418 <p class="text-gray-600 dark:text-gray-300">
1519 AT Protocol (Authenticated Transfer Protocol) is the
1620 foundation of Bluesky and other social apps like
1721 <a href="https://tangled.sh">Tangled</a>,
1818- <a href="https://spark.com">Spark</a>, and more.
1919- Unlike traditional social platforms that lock your
2020- data and identity to a single service, AT Protocol
2121- gives you complete control over your digital presence.
2222- Think of it as an open standard for social networking,
2323- similar to how email works across different providers.
2222+ <a href="https://spark.com">Spark</a>, and more. Unlike
2323+ traditional social platforms that lock your data and identity
2424+ to a single service, AT Protocol gives you complete control
2525+ over your digital presence. Think of it as an open standard
2626+ for social networking, similar to how email works across
2727+ different providers.
2428 </p>
2529 </section>
2630···2832 <h2 class="text-2xl font-semibold mb-4">Key Features</h2>
2933 <ul class="list-disc pl-6 space-y-4 text-gray-600 dark:text-gray-300">
3034 <li>
3131- <strong>PDS Servers:</strong> PDS servers are where your data is stored.
3232- They can be run by anyone, and they are very lightweight, allowing you to
3333- choose which one to use or run your own. PDS servers just store your data,
3434- meaning you don't have to switch PDS servers to use a different app or service.
3535- You can have one PDS while using many different apps and services with the
3535+ <strong>PDS Servers:</strong>{" "}
3636+ PDS servers are where your data is stored. They can be run
3737+ by anyone, and they are very lightweight, allowing you to
3838+ choose which one to use or run your own. PDS servers just
3939+ store your data, meaning you don't have to switch PDS
4040+ servers to use a different app or service. You can have one
4141+ PDS while using many different apps and services with the
3642 same account.
3743 </li>
3844 <li>
3939- <strong>Decentralized Identity:</strong> Your account is tied to a DID
4040- (Decentralized Identifier) rather than your handle/username.
4141- This means you can move your entire account, including your followers
4242- and content, to any PDS by changing where your DID points.
4343- It's also the reason you can use any domain as your handle, because
4444- your identity is not tied to your handle. Your handle can change,
4545+ <strong>Decentralized Identity:</strong>{" "}
4646+ Your account is tied to a DID (Decentralized Identifier)
4747+ rather than your handle/username. This means you can move
4848+ your entire account, including your followers and content,
4949+ to any PDS by changing where your DID points. It's also the
5050+ reason you can use any domain as your handle, because your
5151+ identity is not tied to your handle. Your handle can change,
4552 but your DID will always remain the same.
4653 </li>
4754 <li>
4848- <strong>Portable Content:</strong> All your posts, likes, and other social
4949- data are stored in your Personal Data Server (PDS).
5050- You can switch PDS providers without losing any content or connections.
5555+ <strong>Portable Content:</strong>{" "}
5656+ All your posts, likes, and other social data are stored in
5757+ your Personal Data Server (PDS). You can switch PDS
5858+ providers without losing any content or connections.
5159 </li>
5260 <li>
5353- <strong>Architecture:</strong> The protocol uses a three-tier architecture:
5454- Personal Data Servers (PDS) store your content,
5555- relays broadcast a stream of all events on all PDSes,
5656- and AppViews process and serve that stream into content for users.
5757- This means when you make a post, the content is stored on your PDS,
5858- picked up by relays, and AppViews listen to those relays to deliver
5959- that post to all users.
6161+ <strong>Architecture:</strong>{" "}
6262+ The protocol uses a three-tier architecture: Personal Data
6363+ Servers (PDS) store your content, relays broadcast a stream
6464+ of all events on all PDSes, and AppViews process and serve
6565+ that stream into content for users. This means when you make
6666+ a post, the content is stored on your PDS, picked up by
6767+ relays, and AppViews listen to those relays to deliver that
6868+ post to all users.
6069 </li>
6170 <li>
6262- <strong>Algorithmic Choice:</strong> You're not locked into a single algorithm
6363- for your feed. Different services can offer different ways of curating content,
6464- and you can choose which one you prefer. Bluesky offers a way to make custom
6565- feeds, but even if it didn't, different apps could still offer their own
6666- algorithms for curating content.
7171+ <strong>Algorithmic Choice:</strong>{" "}
7272+ You're not locked into a single algorithm for your feed.
7373+ Different services can offer different ways of curating
7474+ content, and you can choose which one you prefer. Bluesky
7575+ offers a way to make custom feeds, but even if it didn't,
7676+ different apps could still offer their own algorithms for
7777+ curating content.
6778 </li>
6879 </ul>
6980 </section>
···7283 <h2 class="text-2xl font-semibold mb-4">Learn More</h2>
7384 <div class="space-y-4">
7485 <p class="text-gray-600 dark:text-gray-300">
7575- Want to dive deeper into AT Protocol? Check out these resources:
8686+ Want to dive deeper into AT Protocol? Check out these
8787+ resources:
7688 </p>
7789 <ul class="list-none space-y-2">
7890 <li>
7979- <a href="https://atproto.com" class="text-blue-500 hover:underline">Official AT Protocol Docs</a> - The main source for protocol specs and information
9191+ <a
9292+ href="https://atproto.com"
9393+ class="text-blue-500 hover:underline"
9494+ >
9595+ Official AT Protocol Docs
9696+ </a>{" "}
9797+ - The main source for protocol specs and information
8098 </li>
8199 <li>
8282- <a href="https://github.com/bluesky-social/atproto" class="text-blue-500 hover:underline">GitHub Repository</a> - View the protocol implementation
100100+ <a
101101+ href="https://github.com/bluesky-social/atproto"
102102+ class="text-blue-500 hover:underline"
103103+ >
104104+ GitHub Repository
105105+ </a>{" "}
106106+ - View the protocol implementation
83107 </li>
84108 <li>
8585- <a href="https://atproto.wiki" class="text-blue-500 hover:underline">AT Protocol Wiki</a> - Community-driven documentation and resources
109109+ <a
110110+ href="https://atproto.wiki"
111111+ class="text-blue-500 hover:underline"
112112+ >
113113+ AT Protocol Wiki
114114+ </a>{" "}
115115+ - Community-driven documentation and resources
86116 </li>
87117 </ul>
88118 </div>