···77#REDIS_URL=redis://127.0.0.1:6379/
88PROD=true
99# Set's the hostname for oauth
1010-#OAUTH_HOST=advent.codes1010+#OAUTH_HOST=advent.codes
1111+1212+# Challenge account. The account that writes some records for challenges
1313+CHALLENGE_PDS=https://skeetcentral.com
1414+CHALLENGE_IDENTITY=
1515+CHALLENGE_PASSWORD=
···11-Hey! Welcome to at://advent! A 25 day challenge to learn atproto with a new set of challenges every day.
11+Most data on the [atmosphere](https://atproto.com/guides/glossary#atmosphere) is public and stored in things called
22+[records](https://atproto.com/guides/glossary#record) in the
33+user's [repo](https://atproto.com/guides/glossary#data-repo) on the
44+[PDS](https://atproto.com/guides/glossary#pds-personal-data-server). These are stored as key/values in the user's repo
55+divided by [collections](https://atproto.com/guides/glossary#collection). The API shows them as JSON via the api. Like
66+this
77+record of a Bluesky "like"
88+99+```json
1010+{
1111+ "$type": "app.bsky.feed.like",
1212+ "subject": {
1313+ "cid": "bafyreih2odjtzokqgwajtz6ern6h2gsz5humba3thz7hvt6qkj755jvn34",
1414+ "uri": "at://did:plc:hdhoaan3xa3jiuq4fg4mefid/app.bsky.feed.post/3mabnbs3vbs2i"
1515+ },
1616+ "createdAt": "2026-03-17T20:15:52.571Z"
1717+}
1818+```
21933-(Pretend this is going into more details explaining everything)
2020+The records are stored in collections and have record keys. This is usually expressed by an at-uri like
2121+`at://did:plc:rnpkyqnmsw4ipey6eotbdnnf/app.bsky.feed.like/3mhbs2cnrl22r`.
42255-Starting out simple, create a record at the collection `codes.advent.challenge.day`
66-with the record key `1` and put this as the record.
2323+This uri can be broken down into 3 parts:
2424+2525+- `at://did:plc:rnpkyqnmsw4ipey6eotbdnnf` is the DID of the user
2626+- `app.bsky.feed.like` is the collection
2727+- `3mhbs2cnrl22r` is the record key
2828+2929+Using what you learned from day 1 find the following record {generate the at://uri here} and enter the verification code
3030+found in the record
3131+for it below
732833```json
934{
+30-2
shared/src/advent/challenges/day_two.rs
···11-use crate::OAuthAgentType;
21use crate::advent::day::Day;
33-use crate::advent::{AdventChallenge, AdventError, ChallengeCheckResponse};
22+use crate::advent::{
33+ AdventChallenge, AdventError, AdventPart, ChallengeCheckResponse, get_random_token,
44+};
45use crate::atrium::safe_check_unknown_record_parse;
56use crate::lexicons::codes::advent;
77+use crate::{OAuthAgentType, PasswordAgent};
68use async_trait::async_trait;
79use atrium_api::types::Collection;
810use sqlx::PgPool;
···1012pub struct DayTwo {
1113 pub pool: PgPool,
1214 pub oauth_client: Option<OAuthAgentType>,
1515+ pub challenge_agent: Option<PasswordAgent>,
1316}
14171518#[async_trait]
···2629 true
2730 }
28313232+ /// We are overriding the start challenge and using extra code
3333+ async fn start_challenge(&self, did: String, part: AdventPart) -> Result<String, AdventError> {
3434+ let code = get_random_token();
3535+ match part {
3636+ AdventPart::One => sqlx::query(
3737+ "INSERT INTO challenges (user_did, day, time_started, verification_code_one)
3838+ VALUES ($1, $2, NOW(), $3)
3939+ ON CONFLICT (user_did, day)
4040+ DO UPDATE SET verification_code_one = $3
4141+ WHERE challenges.user_did = $1 AND challenges.day = $2",
4242+ ),
4343+ //TODO just going leave these as an update. It should never ideally be an insert
4444+ AdventPart::Two => sqlx::query(
4545+ "UPDATE challenges
4646+ SET verification_code_two = $3
4747+ WHERE challenges.user_did = $1 AND challenges.day = $2",
4848+ ),
4949+ }
5050+ .bind(did)
5151+ .bind(self.day() as i16)
5252+ .bind(code.clone())
5353+ .execute(self.pool())
5454+ .await?;
5555+ Ok(code)
5656+ }
2957 async fn check_part_one(
3058 &self,
3159 did: String,