ryangrey.dev
Ryan Grey — blue rg monogram with cloud and blueprint grid
Open to remote

Ryan Grey

AI & Cloud Operations

AWS-certified systems professional with 10 years in healthcare operations supporting $1B+ in annual billing across 13 hospitals. Builds serverless AWS applications, monitoring, secure CI/CD and native macOS and iOS applications.

AWS Certified Cloud Practitioner badge AWS Certified Cloud PractitionerIssued August 2026 · Verify on Credly → AWS Certified AI Practitioner — in progress, 2026

Technical Skills

Cloud & Operations

AWS LambdaS3EC2VPCCloudFrontRoute 53API GatewayDynamoDBCloudWatchEventBridgeSNSSESCost Explorer & Budgets

Security & Delivery

IAM/STSOIDCCognito/PKCEKMSSSM Parameter StoreGitGitHub Actions CI/CD

AI & Development

Amazon BedrockPythonFastAPIDockerSQLiteJavaScriptBashSwift/SwiftUIMCP

Data & Systems

KPI dashboardsDatabase analysisMicrosoft AccessSharePointEpicCernerHIPAA/PHI

GitHub Activity

Public contribution history from GitHub.

Projects

Live

This Website

Static site served entirely from AWS — the site is its own case study.

Registered domain transferred from GoDaddy to Route 53 (including tearing down a stale DNSSEC chain — DS records deleted at the .dev registry before the nameserver cutover, so resolution never broke). Hosted from S3, fronted by CloudFront with a free ACM certificate. The .dev TLD is HSTS-preloaded, so every connection is HTTPS by force.

  • Keyless CI/CD — OIDC-federated deploys, no stored credentials; every deploy verified byte-for-byte against its rendered page, with stack and contribution checks before deployment credentials are assumed
  • Security headers at the edge — the homepage permits scripts and data requests only from this site; inline scripts and third-party code remain blocked
  • Self-testing monitoring — alarm → SNS → Lambda → SES, with a monthly heartbeat that shares no failure modes with the path it tests

Full write-ups and infrastructure code on GitHub →

Browser Route 53 DNS · ryangrey.dev CloudFront CDN · ACM TLS S3
S3CloudFrontRoute 53ACMDNSSEC
Live

Delivery & Monitoring Pipeline

How this site ships and how it tells on itself when it breaks.

Push-to-main deploys assume a least-privilege IAM role through OIDC federation with zero stored AWS keys, then verify the live page byte-matches the rendered deployment output. GitHub’s undocumented immutable-ID subject claims were diagnosed by decoding the runner’s own JWT.

  • Deployment checks — the stack manifest is checked before AWS credentials are assumed; live page assets and deployed content are verified after publication
  • Security headers at the edge — CloudFront Functions allow the homepage’s own contribution tracker while blocking inline scripts, third-party code, and external data requests
  • Mail that actually arrives — a CloudFront 5xx alarm routed through Lambda to SES, signing DKIM as ryangrey.dev via Route 53, so DMARC passes on DKIM alignment where SPF cannot align; built after tracing silent drops to a provider accepting mail and discarding it
  • Monitoring that tests itself — an EventBridge Scheduler heartbeat force-fires that alarm monthly through a path sharing no failure modes with the pipeline it tests, behind an AWS Budgets guardrail

Full write-ups and infrastructure code on GitHub →

GitHub ActionsIAM OIDCCloudFront FunctionsCloudWatchSNSLambdaSESEventBridge
TestFlight

GreyScale — Family Weigh-In on iOS

Private iOS app and serverless backend · no public listing and no public repo, by design

A shared spreadsheet had been scoring a four-person family’s weekly weigh-ins since May, and its computed columns had already gone stale. GreyScale replaces it: a SwiftUI app on everyone’s phone over a serverless backend, designed, built and shipped solo. Single-table DynamoDB reached only by Query on the partition key — there is no Scan in the code, and none granted in the IAM policy either. It has since grown a calorie tracker with five ways in: typed, barcode, database search, a photograph of the plate read by Bedrock, and a Nutrition Facts panel read on the phone itself.

  • Nothing derived is ever stored — the table holds a weight and a date; every weekly loss, running total and pass/fail verdict is recomputed from raw weights. A later “rewind to any past week” feature then cost almost nothing to build: showing week 5 is scoring the same rows with the later ones removed, so there is no historical snapshot that can drift out of step
  • Every write is pinned to its author — no endpoint accepts a member id, not the weigh-in, the photo or the profile badges. The row key always derives from the verified token’s username claim, so tampering has nothing to aim at
  • Rejected before the code runs — an API Gateway JWT authorizer turns away unauthenticated calls, and the handler then re-checks token_use independently, because ID and access tokens are signed by the same pool and only one of them is meant to be a bearer credential. Sign-in is Authorization Code with PKCE on a public client holding no secret
  • Unknown never renders as zero — a photographed meal is uploaded straight to S3 on a presigned URL, fanned through EventBridge and SQS to a second Lambda with its own role, and read by Bedrock Nova Lite. The card appears first, contributing nothing until an answer lands; a failure shows as failed with a retry, never as a meal worth no calories. The model’s output is gated on food, confidence and plausibility before it is believed — fed the app’s own icon it had confidently returned “Food, 100 kcal”
  • Not everything needs a model — a Nutrition Facts panel is printed text, so it is read on the device by Vision instead: no upload, no queue, no per-use cost and an answer in under a second. Rendering a realistic panel and running the real recogniser over it exposed a bug no hand-written test had — the figure can be returned above the word “Calories”, not below
  • The interesting number is derived, not recorded — true maintenance is back-solved from intake against what the scale actually did, over every window of three weeks or more, with the longest as the headline. Short windows are refused on principle: on real data a one-week back-solve returned 3,488 cal/day against a true figure near 2,550, which is not merely wrong but confidently wrong
  • Checked against what it replaced, not against itself — the test suite asserts the app reproduces every weekly and running total the original spreadsheet computed, restates each verdict independently, and re-derives seven published weekly averages from the log it inherited. It runs without Xcode or a simulator, so the part most worth being right about is verifiable before the project will even open. Views are rendered offline to PNG and looked at — every view checked that way had a bug the render caught

Delivered on a split toolchain. macOS 27 refused to launch the release Xcode at all, so archive, code signing and export ran headlessly through xcodebuild, with a second Xcode installed alongside solely for account setup and upload — builds stayed on the release toolchain because App Store Connect rejects binaries built with beta tooling.

Four screens of GreyScale: the weekly GREEN or RED verdict for each member, calories by week as seven-bar charts with the over-goal portion in red, the maintenance back-solve with its window table, and the achievements grid.
The weekly verdict, calories by week, what the scale says that food actually cost, and the record it adds up to. Everybody here is invented: the app is built with a fixture in place of its data layer, because the real screens carry the family’s names and photographs. The avatars are the app’s own fallback for a member who has not set a picture.
SwiftSwiftUISwift ChartsiOSVisionVisionKitAmazon CognitoPKCEAPI GatewayLambdaDynamoDBAmazon BedrockS3SQSEventBridgeTestFlight
Shipped

Cloud Watchtower — macOS Menu-Bar Monitor

A native menu-bar app that answers two questions at a glance: is the site healthy, and what is it costing? CloudFront traffic and error rates, CloudWatch alarm state, and month-to-date spend against the budget. The menu-bar glyph encodes health, so the panel only needs opening when something is actually wrong.

  • No dependencies at all — the AWS SDK for Swift is a 2.3 GB repository, so request signing is written directly instead: AWS SigV4 in about a hundred lines of CryptoKit, covering both the Query/XML and JSON wire protocols
  • Cost is a design constraint — Cost Explorer bills per request, so it never runs on a timer and sits behind a button labelled with its own price; batching every metric into a single call brings the monitor to ~$0.12/month against roughly $8.50 for a poll-everything build — over 60× cheaper
  • Least privilege — it assumes a scoped read-only role on one-hour STS credentials rather than holding the deploy key, and proves which identity it resolved instead of trusting the config
  • Degrades honestly — a failed refresh keeps the last good value with the failure named and its age stated, and the glyph has a third state, because “cannot reach AWS” is not the same as healthy

Caught all four alarm state transitions on its first live test. Then it earned its keep immediately — it surfaced a sustained 4xx error rate that the existing 5xx-only alarm was structurally unable to see, traced to a missing favicon returning 403 from the origin and fixed the same day. It kept earning it: the panel below still reads about 32%, which is the same signal saying the favicon was one instance of a general fault. An S3 origin behind an origin access control returns 403, not 404, for anything absent — so every automatic request for a file that was never there, robots.txt and sitemap.xml included, counted against the rate. Both now exist. What is left is unsolicited probing for things like /wp-login.php, which should be refused — the honest target for this number is not zero.

The Watchtower panel in light and dark mode, styled after GitHub's Primer, showing CloudFront request counts and error rates, the CloudWatch alarm reading OK, and month-to-date spend against the budget.
The panel, light and dark, on live data, rendered by the app itself rather than screenshotted. The footer is the monitor auditing its own bill: every AWS call it has made since launch, counted and priced, so the ~$0.12/month claim above is measured rather than estimated.
SwiftSwiftUImacOSCloudWatchCost ExplorerIAM & STSAWS SigV4
Live

greyBot — Discord Administration & Raid Operations

Built a Discord operations platform spanning server administration, moderation, member verification, raid signups and searchable guild history. Persistent Docker services run the administrator website and event collector, while AWS Lambda and EventBridge handle raid announcements and recap generation for three teams.

  • Authorization on every private request — Discord OAuth identifies the administrator, then live guild permissions determine access. Moderation requests recheck permissions and role hierarchy; session, CSRF and origin checks protect the web controls
  • Searchable history with explicit limits — a SQLite message index and hash-chained event journal support named member/channel filters, role changes and moderation review. Capture gaps remain visible; the journal is tamper-evident, not a claim of immutable or complete history
  • Membership and moderation workflows — verification, controlled role assignment, timeout/kick/ban requests, persistent mutes and configurable spam handling share an administrator interface. Member channel preferences can hide eligible channels without granting new access
  • Raid signups and reporting — members choose classes, specializations and attendance status; organizers review rosters and past responses. Scheduled reporting publishes first-kill cards and per-night recaps, with conditional-write claims to prevent overlapping runs from posting the same announcement
  • One source, distinct runtime boundaries — the consolidated release contains the Docker service, AWS raid runtime and local MCP reader integrations. Personal desktop captures remain local and outside hosted guild search

Deployed as a hybrid Docker and AWS application, with regression checks for authorization, history handling, local integrations and raid reporting. Public project information is separate from the administrator-only dashboard; automation settings and event coverage are tracked explicitly.

greyBot event history interface with synthetic members River and Sky, role-change records, filters and journal status.
Event history in the actual administrator interface, rendered locally with synthetic members and events. Observe-only mode and the unconfigured archive shown here belong to the isolated demo, not the production server. View full-size screenshot.
greyBot raid signup interface showing a synthetic roster, class and specialization choices, attendance controls and organizer actions.
Raid signup controls and roster, rendered from the application with an invented event and synthetic participants. No private server messages or real member profiles are shown. View full-size screenshot.
Three greyBot first-kill cards beside a generated raid recap with damage, healing and performance summaries.
The existing raid reporting workflow: generated first-kill cards and a nightly recap, using public raid-log data.
Python & FastAPIDockerSQLiteDiscord OAuth & GatewayLambdaEventBridgeDynamoDBS3 & CloudFrontMCP
Live

Multi-Client MCP Integrations — Claude & Codex

Python · four local servers · three client registrations per server

Adapted the Messages, Contacts, GreyScale and Discord integrations to work across Claude Desktop, Claude Code and Codex. A shared configuration layer connects each client to the same server implementations and virtual environments, so adding another client does not require a separate fork of each server.

  • One definition, consistent registrations — a credential-free manifest drives a Python synchronizer that previews drift, backs up affected settings, preserves unrelated entries and verifies the registrations after applying changes
  • Parallel sessions, explicit boundaries — independent clients initialize the servers and enumerate their tools concurrently; read probes verify access separately from a successful connection, without claiming that simultaneous writes have been certified
  • Permissions stay with the host — existing write-confirmation gates remain in place, GreyScale exposes read-only tools, and macOS permissions still govern Messages and Contacts access from each hosting application

All twelve registrations match the shared definitions, and repeat synchronization reports no drift. The same integrations can be used from either assistant without maintaining separate server code.

PythonMCPJSONTOMLmacOS
Live

imessage-mcp — Apple Messages & Contacts for Claude & Codex

Local MCP servers for Claude and Codex to search, read and send Apple Messages, and manage Apple Contacts — list, dedupe, merge, and move cards between accounts — subject to the hosting application’s macOS permissions. Everything stays on the machine: the message database is opened strictly read-only, sending goes through Messages.app itself, contacts go through Apple’s own framework, and there is no code path that touches the network.

  • Read-only by construction — chat.db is opened with SQLite’s mode=ro&immutable=1, which takes no locks at all, so a query can never block Messages.app; nothing under the Messages directory is ever written
  • The text isn’t where it says it is — modern macOS leaves the text column NULL and stores the body only as an archived attributed string, so bodies are decoded from the typedstream blob, and a status tool reports how often that decode is actually working rather than letting search silently return empties
  • Search that doesn’t rescan history — decoded text feeds a local SQLite FTS5 index rebuilt incrementally by ROWID, so the first search pays for the backlog once and every later one indexes only the delta
  • Sending is deliberately harder than reading — a send refuses without an explicit confirm flag, goes through Messages.app via AppleScript rather than any private API, and every attempt lands in a local audit log
  • Contacts through the front door — the Contacts server drives CNContactStore via PyObjC, the same path Contacts.app uses, never the AddressBook SQLite underneath; every write is confirm-gated and logged with the full before/after card, and the iCloud container is resolved structurally rather than as “the default account”, so a merged or moved card can never silently land in the wrong service

Tests run against a fully synthetic fixture database and an in-memory fake of the Contacts layer, because the honest way to publish a tool that reads your texts and your address book is a repository that provably contains neither.

PythonMCPSQLiteFTS5AppleScriptPyObjCContacts.frameworkmacOS
Live

discord-mcp — Discord for Claude & Codex, with provenance

Discord’s API does not let one thing see everything. A bot sees only the servers it has been invited to and can never read direct messages; the only way around that is automating your own user account, which Discord bans. So this is a local MCP server that combines the sources that are allowed — a bot, the official data-request export, and a capture-only client plugin — into one SQLite FTS5 index, tags every row with where it came from, and refuses cleanly wherever no compliant path exists.

  • Three sources, one row per message — the message’s snowflake id is the primary key, so a message seen by two sources exists once and the fuller source wins (bot over capture over export); re-importing the export, re-syncing the bot, or restarting a backfill is idempotent by construction, not by bookkeeping
  • Writes only where the bot can act — send, edit and delete work as the bot, require an explicit confirm flag, log before-and-after to an audit file, and refuse a channel known only from the export or capture sources with a message that says why: there is no permitted way to write as yourself
  • The silent failure made loud — a bot without the Message Content intent connects fine and indexes every message as empty text, so the status tool reads the intent flag off the application itself and the sync warns when nearly every fetched row comes back blank
  • Honest about the export — Discord’s data package contains only the messages you sent, not the replies; the README carries the coverage table saying which combination of sources can and cannot reach each kind of message, rather than implying completeness
  • The capture side never sends — the companion Vencord plugin listens to the client’s own message events and appends them to a file from Electron’s main process; a launchd job folds that into SQLite with a change counter so edits and deletions flow through too. Its one opt-in step past passive capture — a paced page-by-page backfill of a conversation, with a time-based ETA, a persisted resume point, and a main-process timer so a minimized window keeps pace — is documented as exactly that, alongside the client-modification risk it carries

First real run indexed 103,730 messages across 35 channels in one evening. The repository contains none of them: fixtures are synthetic, the bot token lives in SSM Parameter Store and is never written to disk by the code, and the tests assert that a second import adds nothing.

PythonMCPDiscord APISQLiteFTS5SSMTypeScriptVencordElectronlaunchd

Experience

Owner — E-Commerce Operations — self-employed · eBay & Facebook Marketplace2019 – present

Resale business across eBay and Facebook Marketplace — market-comp research, pricing strategy, listing copy, and negotiation across 40+ concurrent listings. Computer science coursework (Franklin University) and Sabio full-stack bootcamp training undertaken concurrently.

Medical Records / HIM Systems Coordinator — Ballad Health (formerly Mountain States Health Alliance)2017 – 2019

Managed 3 direct reports with oversight of 18 coders; improved KPI performance across Epic, Cerner, SMS Envision and 3M platforms while maintaining HIPAA/PHI compliance; streamlined data collection and reporting behind $1B+ in annual billing across 13 hospitals by authoring standardized templates and centralized dashboards.

Billing Applications Coordinator — Ballad Health2012 – 2017

Sole support for six revenue-cycle systems (PayNav, Audit Logix, SharePoint, Chargemaster, US Bank, SCI); built the front-end revenue-cycle dashboard reported daily to CFOs; designed and built the department's precertification tracking database in Microsoft Access — front and back end, used by 40+ staff — and ran it in production with monthly backups, with its reports going directly to executive leadership.

Patient Access Representative — Ballad Health2009 – 2012

Registration and insurance eligibility (Medicare, Medicaid, BCBS) for up to 100 patients daily; trained new team members across facilities during software launches.

Full Resume (PDF) →

Education & Professional Development

AWS Skill Builder — AI Practitioner Learning Plan & Exam Prep Plan (~30 hrs) · Cloud Practitioner exam track2026
Franklin University — Computer science coursework toward B.S.
Sabio Coding Bootcamp — Full-Stack Developer program — not completed2024
East Tennessee State University — Computer science coursework