diff --git a/shared/challenges_markdown/five/part_one.md b/shared/challenges_markdown/five/part_one.md
index 640ea85..e17c4e1 100644
--- a/shared/challenges_markdown/five/part_one.md
+++ b/shared/challenges_markdown/five/part_one.md
@@ -1,2 +1,15 @@
-Day five is a only one part one. We talk about jetstream/firehose. A secret account creates a record and they have to
-watch create/update to capture the verification code. We hav an added button when clicked it creates the record.
\ No newline at end of file
+All events on the atmosphere are broadcast as events on
+the [Event Stream](https://atproto.com/specs/event-stream). Every lexicon you create is sent from a websocket
+on your PDS, picked up by a relay and then distributed to the rest of the network. This is usually referred to as
+the [firehose](https://docs.bsky.app/docs/advanced-guides/firehose). There is also
+the [jetstream](https://atproto.com/blog/jetstream) that simplifies the firehose by broadcasting the events in json and
+strips the authenticated portion to keep things simple. The jetstream also has the advantage of allowing you to filter
+by `collection` so you only get the events you are interested in.
+
+Today's challenge will be to find a record that is created by a secret account. When you click the link below our secret
+account will create a `codes.advent.challenge.shhh` record with your did as the `subject` field and will hold today's
+verificaiton code to enter below. This will come across as a create or update event and you can click the button below
+to create the record as many times as you need.
+
+Create the record (will create as many as you need)
+
diff --git a/shared/lexicons/codes/advent/shhh.json b/shared/lexicons/codes/advent/shhh.json
new file mode 100644
index 0000000..6d4a84f
--- /dev/null
+++ b/shared/lexicons/codes/advent/shhh.json
@@ -0,0 +1,31 @@
+{
+ "lexicon": 1,
+ "id": "codes.advent.challenge.shhh",
+ "defs": {
+ "main": {
+ "type": "record",
+ "key": "tid",
+ "record": {
+ "type": "object",
+ "required": [
+ "secretPartOne",
+ "subject",
+ "createdAt"
+ ],
+ "properties": {
+ "secretPartOne": {
+ "type": "string"
+ },
+ "subject": {
+ "type": "string",
+ "format": "did"
+ },
+ "createdAt": {
+ "type": "string",
+ "format": "datetime"
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/shared/src/advent/challenges/day_five.rs b/shared/src/advent/challenges/day_five.rs
index ca5fd79..e45048c 100644
--- a/shared/src/advent/challenges/day_five.rs
+++ b/shared/src/advent/challenges/day_five.rs
@@ -1,12 +1,88 @@
-use crate::OAuthAgentType;
use crate::advent::day::Day;
-use crate::advent::{AdventChallenge, AdventError, ChallengeCheckResponse};
+use crate::advent::{AdventChallenge, AdventError, AdventPart, ChallengeCheckResponse};
+use crate::lexicons::codes::advent;
+use crate::lexicons::record::KnownRecord;
+use crate::{OAuthAgentType, PasswordAgent};
use async_trait::async_trait;
+use atrium_api::types::Collection;
+use atrium_api::types::string::Tid;
+use serde_json::json;
use sqlx::PgPool;
pub struct DayFive {
pub pool: PgPool,
pub oauth_client: Option,
+ pub secret_agent: Option,
+}
+
+impl DayFive {
+ /// Creates the challenge record on the secret agent's repo with the user's verification code.
+ /// This is called from the `/day/5/{did}` endpoint.
+ pub async fn create_secret_record(&self, did: &str) -> Result<(), AdventError> {
+ let Some(agent) = &self.secret_agent else {
+ log::warn!("No secret agent configured, skipping record creation for day five");
+ return Err(AdventError::ShouldNotHappen(
+ "No secret agent configured".to_string(),
+ ));
+ };
+
+ // Get the user's challenge to find their verification code
+ let challenge = self.get_days_challenge(did).await?.ok_or_else(|| {
+ AdventError::ShouldNotHappen("Could not find challenge record for day 5".to_string())
+ })?;
+
+ let code = challenge.verification_code_one.ok_or_else(|| {
+ AdventError::ShouldNotHappen(
+ "No verification code found for day 5 challenge".to_string(),
+ )
+ })?;
+
+ let agent_did = agent
+ .did()
+ .await
+ .ok_or_else(|| AdventError::ShouldNotHappen("Secret agent has no DID".to_string()))?;
+
+ let record_data = advent::challenge::shhh::RecordData {
+ secret_part_one: code,
+ created_at: atrium_api::types::string::Datetime::now(),
+ subject: did.parse().unwrap(),
+ };
+ let known_record: KnownRecord = record_data.into();
+ let record_value: atrium_api::types::Unknown = known_record.into();
+
+ let tid = Tid::from_datetime(23.try_into().unwrap(), challenge.time_started);
+ let result = agent
+ .api
+ .com
+ .atproto
+ .repo
+ .put_record(
+ atrium_api::com::atproto::repo::put_record::InputData {
+ collection: advent::challenge::Shhh::NSID.parse().unwrap(),
+ repo: agent_did.as_ref().parse().unwrap(),
+ rkey: tid.as_ref().parse().unwrap(),
+ swap_record: None,
+ record: record_value,
+ swap_commit: None,
+ validate: Some(false),
+ }
+ .into(),
+ )
+ .await;
+
+ match result {
+ Ok(_) => {
+ log::info!("Created secret record for day 5 for user: {did}");
+ Ok(())
+ }
+ Err(e) => {
+ log::error!("Failed to create secret record for day 5: {e}");
+ Err(AdventError::ShouldNotHappen(format!(
+ "Failed to create secret record: {e}"
+ )))
+ }
+ }
+ }
}
#[async_trait]
@@ -27,11 +103,49 @@ impl AdventChallenge for DayFive {
true
}
+ async fn build_additional_context(
+ &self,
+ did: &str,
+ part: &AdventPart,
+ _code: &str,
+ ) -> Result