aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Korotkiewicz <skorotkiewicz@gmail.com>2026-08-10 03:20:04 +0200
committerSebastian Korotkiewicz <skorotkiewicz@gmail.com>2026-08-10 03:20:04 +0200
signature Sebastian Korotkiewicz <skorotkiewicz@gmail.com>

This commit was signed with a verified signature.

Signer
Sebastian Korotkiewicz <skorotkiewicz@gmail.com>
Signing key
5BDC557B496BDB0D
Fingerprint
B498E2E410902F8AEC108F4F5BDC557B496BDB0D
commit4c38b8abcaf0ea90b0660162d9e485dbbba5e791 (patch)
treefe5e399d65e4fa3701843110f02447ba992f64de
parent672fec0852fb45410b6d2e58bce17469768fa98a (diff)
downloadaurcade-4c38b8abcaf0ea90b0660162d9e485dbbba5e791.tar.gz
aurcade-4c38b8abcaf0ea90b0660162d9e485dbbba5e791.zip
Add verified SSH activity calendar to lobby
The SSH lobby now displays a 53-week contribution calendar showing only commits on repository default branches with trusted SSH signatures belonging to the account. Commits are verified using Git's GPG SSH verification against the allowed_signers file.
-rw-r--r--README.md2
-rw-r--r--src/main.rs105
2 files changed, 106 insertions, 1 deletions
diff --git a/README.md b/README.md
index 795adc8..6048ff2 100644
--- a/README.md
+++ b/README.md
@@ -76,7 +76,7 @@ paths = ["example", "team/tools"] # An exact path grants access to one repositor
paths = ["alice/"] # Namespace; creates repositories on first push.
```
-Connect without a Git command to open the account's SSH lobby:
+Connect without a Git command to open the account's SSH lobby. Its 53-week activity calendar counts only commits on repository default branches with trusted SSH signatures belonging to that account:
```sh
ssh -p 2222 git@localhost
diff --git a/src/main.rs b/src/main.rs
index cc99aee..df59026 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -512,6 +512,107 @@ fn cleanup_failed_creation(path: &Path, created: bool, success: bool) -> Result<
Ok(())
}
+fn verified_ssh_activity(
+ account: &Account,
+ root: &Path,
+ repositories: &BTreeSet<String>,
+) -> Result<([u32; 371], i64), Error> {
+ let today = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() as i64 / 86_400;
+ let start = today - (today + 4).rem_euclid(7) - 52 * 7;
+ let allowed_signers = signing_root().join("allowed_signers");
+ let mut counts = [0_u32; 371];
+ let mut commits = HashSet::new();
+
+ // ponytail: verify on demand; cache by commit ID if large histories slow down the lobby.
+ for repository in repositories {
+ let path = root.join(format!("{repository}.git"));
+ let output = Command::new("git")
+ .arg("-c")
+ .arg(format!(
+ "gpg.ssh.allowedSignersFile={}",
+ allowed_signers.display()
+ ))
+ .arg("--git-dir")
+ .arg(path)
+ .args([
+ "log",
+ "--since=371.days.ago",
+ "--format=%H%x09%ct%x09%G?%x09%GS%x09%GF",
+ "HEAD",
+ ])
+ .stderr(Stdio::null())
+ .output()?;
+ if !output.status.success() {
+ continue;
+ }
+ for line in String::from_utf8_lossy(&output.stdout).lines() {
+ let mut fields = line.split('\t');
+ let (Some(commit), Some(timestamp), Some(status), Some(signer), Some(fingerprint)) = (
+ fields.next(),
+ fields.next(),
+ fields.next(),
+ fields.next(),
+ fields.next(),
+ ) else {
+ continue;
+ };
+ if fields.next().is_some()
+ || status != "G"
+ || signer != account.name
+ || !fingerprint.starts_with("SHA256:")
+ {
+ continue;
+ }
+ let Ok(day) = timestamp
+ .parse::<i64>()
+ .map(|timestamp| timestamp.div_euclid(86_400))
+ else {
+ continue;
+ };
+ if day >= start && day <= today && commits.insert(commit.to_owned()) {
+ counts[(day - start) as usize] += 1;
+ }
+ }
+ }
+ Ok((counts, today))
+}
+
+fn render_ssh_calendar(counts: &[u32; 371], today: i64) -> String {
+ let start = today - (today + 4).rem_euclid(7) - 52 * 7;
+ let total: u32 = counts.iter().sum();
+ let label = if total == 1 {
+ "CONTRIBUTION"
+ } else {
+ "CONTRIBUTIONS"
+ };
+ let mut output = format!("\nVERIFIED SSH ACTIVITY // {total} {label} // LAST 53 WEEKS\n");
+ for (weekday, label) in ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
+ .iter()
+ .enumerate()
+ {
+ output.push_str(label);
+ output.push_str(" ");
+ for week in 0..53 {
+ let index = week * 7 + weekday;
+ let symbol = if start + index as i64 > today {
+ ' '
+ } else {
+ match counts[index] {
+ 0 => '·',
+ 1 => '░',
+ 2..=3 => '▒',
+ 4..=7 => '▓',
+ _ => '█',
+ }
+ };
+ output.push(symbol);
+ }
+ output.push('\n');
+ }
+ output.push_str(" Less · ░ ▒ ▓ █ More\n");
+ output
+}
+
fn ssh_lobby(config: &Config, account: &Account, root: &Path) -> Result<String, Error> {
let mut repositories = BTreeSet::new();
configured_repositories(config, root, root, &mut repositories)?;
@@ -551,6 +652,8 @@ fn ssh_lobby(config: &Config, account: &Account, root: &Path) -> Result<String,
));
}
}
+ let (activity, today) = verified_ssh_activity(account, root, &repositories)?;
+ output.push_str(&render_ssh_calendar(&activity, today));
output.push_str("\nNO SHELL. ONLY GIT. GAME ON.\n");
Ok(output)
}
@@ -759,6 +862,8 @@ mod tests {
assert!(output.contains("02 team/tools [CO-OP]"));
assert!(output.contains("https://git.example/alice/game"));
assert!(output.contains("ssh://git@git.example/team/tools"));
+ assert!(output.contains("VERIFIED SSH ACTIVITY // 0 CONTRIBUTIONS // LAST 53 WEEKS"));
+ assert!(output.contains("Less · ░ ▒ ▓ █ More"));
assert!(!output.contains("bob/private"));
assert!(!output.contains("hidden"));
fs::remove_dir_all(root).unwrap();