Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 13 additions & 12 deletions .agents/skills/conforming-to-agents-md/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,19 +72,20 @@ and line are duplicates regardless of rule; keep the one whose rule is an
collapse into one record listing every line. Order by rule violations first,
then dead code, then slop.

**4. Fix.** Spawn fixer agents serially, never two at once. With 20 or fewer
findings, one fixer per finding. Above 20, one fixer per file, given every
finding in that file. A fixer receives its findings, the merged file path for
context, the rule text each violates, and this instruction:
**4. Fix.** Spawn fixers in parallel when their fixes touch disjoint files, and
combine findings that touch the same file into one fixer. A fixer receives its
findings, the files it owns, the merged file path for context, the rule text
each violates, and this instruction:

> Fix these findings completely. Make whatever refactor the fix genuinely needs,
> including changes outside the cited lines. Do not add doc comments to items you
> did not create. Delete a unit test as covered by an e2e test only when, by
> reading the e2e test, every assertion of the unit test is provably covered; you
> cannot run the e2e suite. Then run `cargo fmt --all`, `cargo clippy --workspace
> --all-targets --locked -- -D warnings`, and `cargo test --workspace --locked`.
> Return one paragraph per finding saying what changed, including any change
> beyond the cited lines. Do not commit.
> including changes outside the cited lines, editing only these files:
> <file list>. Do not add doc comments to items you did not create. Delete a
> unit test as covered by an e2e test only when, by reading the e2e test, every
> assertion of the unit test is provably covered; you cannot run the e2e suite.
> Then run `cargo fmt --all`, `cargo clippy --workspace --all-targets --locked
> -- -D warnings`, and `cargo test --workspace --locked`. Return one paragraph
> per finding saying what changed, including any change beyond the cited lines.
> Do not commit.

If a fixer reports a finding was wrong, record why in the round file and carry
it forward as adjudicated.
Expand Down Expand Up @@ -119,7 +120,7 @@ Stop and re-read this skill if you notice yourself:

- Reporting findings to the user instead of fixing them
- Fixing findings yourself inline instead of spawning a fixer
- Running two fixers at once
- Running two fixers on the same file at once
- Letting a fixer tell you the tree is clean instead of re-sweeping
- Keeping a hedged finding because "it's probably right"
- Overriding an `AGENTS.md` rule because the fix looks worse; the rule is the
Expand Down
25 changes: 13 additions & 12 deletions .agents/skills/refactoring-iteratively/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,18 @@ candidate records in the shape below.
Order by payoff over blast radius: a one-file change that removes a duplicate
outranks a five-file change that tightens a type.

**3. Apply.** Spawn fixers serially, never two at once. With 20 or fewer
candidates, one fixer per candidate. Above 20, one fixer per file, given every
candidate in that file. A fixer receives its candidates, the merged file path
for context, and:

> Apply these refactors completely, including every call site they touch. Preserve
> behavior, public signatures, serialized shapes, and error text. Do not add doc
> comments to items you did not create. Then run `cargo fmt --all`, `cargo clippy
> --workspace --all-targets --locked -- -D warnings`, and `cargo test --workspace
> --locked`. If a test fails, revert your change with `git checkout -- <files>`
> and report why. Return one paragraph per candidate. Do not commit.
**3. Apply.** Spawn fixers in parallel when their changes touch disjoint files,
and combine candidates that touch the same file into one fixer, in rank order.
A fixer receives its candidates, the files it owns, the merged file path for
context, and:

> Apply these refactors completely, including every call site they touch,
> editing only these files: <file list>. Preserve behavior, public signatures,
> serialized shapes, and error text. Do not add doc comments to items you did
> not create. Then run `cargo fmt --all`, `cargo clippy --workspace
> --all-targets --locked -- -D warnings`, and `cargo test --workspace --locked`.
> If a test fails, revert your change with `git checkout -- <files>` and report
> why. Return one paragraph per candidate. Do not commit.

**4. Re-scan.** Go to step 1 with fresh reviewers. Stop when the ranked list is
empty, or after round 4. After round 4, report the open candidates instead of
Expand All @@ -95,7 +96,7 @@ change: Store `Uuid` in `TurnkeySigner`; call `.to_string()` once at the wire ca

- A payoff sentence with "cleaner", "clearer", "more idiomatic", or "better"
- Applying a candidate the reviewer rated as marginal to have something to do
- Running fixers in parallel
- Running two fixers on the same file at once
- Skipping `cargo test` because clippy passed
- Stopping because the remaining candidates are small, rather than because there are none
- Committing, amending, or pushing
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ reqwest = { version = "0.12", default-features = false, features = ["json", "rus
rpassword = { version = "7.4", default-features = false }
serde = { version = "1.0.229", default-features = false, features = ["std", "derive"] }
serde_json = { version = "1.0.151", default-features = false, features = ["std"] }
sha1 = { version = "0.10", default-features = false }
sha2 = { version = "0.11.0", default-features = false }
strum = { version = "0.28.0", default-features = false, features = ["derive", "std", "strum_macros"] }
tempfile = { version = "3.27.0", default-features = false }
Expand Down
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
# `tk`

Experimental Turnkey auth workspace centered on the `tk` CLI.

`tk` is focused on general agent authorization, attribution, and credential management with Turnkey backed keys.

- [Turnkey account administration](./docs/core.md)
- [Secrets manager](./docs/secrets.md)
- [Git signing](./docs/git-signing.md)
- [SSH agent](./docs/ssh-agent.md)

> Warning: `tk` is experimental and has not been audited.
- [GPG signing](./docs/gpg-signing.md)

## Installation

Expand All @@ -26,6 +25,12 @@ tk config
tk ssh public-key
tk ssh git-sign
tk ssh agent
tk gpg keys create
tk gpg keys add
tk gpg keys remove
tk gpg keys list
tk gpg keys export
tk gpg sign
```

## Configuration
Expand Down Expand Up @@ -69,3 +74,9 @@ export TURNKEY_API_BASE_URL="https://api.turnkey.com" # optional
```

These environment variables override values stored in the global config file. This can be helpful for CI.

### GPG Environment

- `TK_GPG_PROGRAM` names the real GnuPG binary the git shim runs for verification calls.

Registered OpenPGP keys live in the identity registry under `gpg_keys`, keyed by fingerprint. See [GPG signing](./docs/gpg-signing.md).
1 change: 1 addition & 0 deletions crates/auth/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ anyhow = { workspace = true }
base64 = { workspace = true }
hex = { workspace = true }
serde = { workspace = true, features = ["derive"] }
sha1 = { workspace = true }
sha2 = { workspace = true }
tokio = { workspace = true, features = ["fs", "io-util", "macros", "net", "rt", "signal", "time"] }
toml = { workspace = true }
Expand Down
1 change: 1 addition & 0 deletions crates/auth/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
pub mod config;
pub mod errors;
pub mod git_sign;
pub mod openpgp;
pub mod public_key;
pub mod ssh;
pub(crate) mod turnkey;
64 changes: 64 additions & 0 deletions crates/auth/src/openpgp/armor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//! ASCII armor for `OpenPGP` objects (RFC 4880 section 6).

use std::fmt::{self, Display, Formatter};

use base64::{Engine, engine::general_purpose::STANDARD};

/// RFC 4880 6.1.
const CRC24_INIT: u32 = 0x00b7_04ce;
const CRC24_POLY: u32 = 0x0086_4cfb;
const ARMOR_LINE_LENGTH: usize = 64;

/// The armor header label (RFC 4880 6.2).
pub(crate) enum BlockType {
PublicKeyBlock,
Signature,
}

impl Display for BlockType {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::PublicKeyBlock => "PUBLIC KEY BLOCK",
Self::Signature => "SIGNATURE",
})
}
}

fn crc24(data: &[u8]) -> u32 {
let mut crc = CRC24_INIT;
for &byte in data {
crc ^= (byte as u32) << 16;
for _ in 0..8 {
let top_bit_set = crc & 0x0080_0000 != 0;
crc = (crc << 1) & 0x00ff_ffff;
if top_bit_set {
crc ^= CRC24_POLY;
}
}
}
crc
}

/// Lines are `\n` separated and the result ends with a newline.
pub(crate) fn armor(block_type: BlockType, data: &[u8]) -> String {
let encoded = STANDARD.encode(data);
let mut body = String::new();
let mut rest = encoded.as_str();
while !rest.is_empty() {
let (line, tail) = rest.split_at(rest.len().min(ARMOR_LINE_LENGTH));
body.push_str(line);
body.push('\n');
rest = tail;
}

let crc = crc24(data);
let checksum = STANDARD.encode([(crc >> 16) as u8, (crc >> 8) as u8, crc as u8]);

format!(
r#"-----BEGIN PGP {block_type}-----

{body}={checksum}
-----END PGP {block_type}-----
"#
)
}
Loading
Loading