Note5 min@codycollier

A typeface that keeps a secret

Paste an API key while you’re sharing your screen, and everyone on the call has it. So does the recording, the screenshot in the bug report, and any agent that works by looking at your screen.

You could fix that inside one app. But there’s one thing nearly every app on your screen already uses: a font. So I put the fix there. Paste a key in here:

No plugin, no extension, nothing for the app to know about. Here’s how a font can do that.

A font is a small program

We think of a font as a set of drawings. It’s also a set of rules. Each time text appears, a piece of your system called a shaper reads the font’s rules and may swap one glyph for another. It’s how a font joins f and i into a ligature.

Nothing says a rule has to be about typography.

You typeghp_aB3dEFGHiJ7kLm
The font’s rulesSwap glyphs
The screen showsghp_aB3dEFGHiJ7kLm

It can’t look ahead

The obvious plan is to wait for the whole key, then hide it. But text arrives one character at a time, typed by you or written by a model. Waiting means the key sits there readable, then flickers away.

So every rule looks only backward. The moment a character is drawn, it’s final, and nothing on screen ever changes. Stop the stream anywhere, and what you see is already right.

keystrokes
20
dots
12
redrawn
0
The shaper’s real output for every keystroke of this key. Once a cell is drawn, it never changes.

A note in the shape of an underscore

Looking only backward creates a problem. A shaper has no memory. All a rule can read is the glyphs behind it. So how does a character further along know it’s inside a key?

The font leaves itself a note, and hides it in plain sight.

=underscoreunderscore.sec.alnum.loweran underscore“a key starts here”
Two different glyphs with the same drawing. You can’t tell them apart. The rules can.

When the font sees ghp_, GitHub’s prefix, it swaps that underscore for its twin. From then on, the note does the work:

  1. The note. The twin marks where a key begins. It also works for the = in TOKEN=.
  2. The window. The next four characters stay readable, so you can still tell your keys apart.
  3. The dots. A character becomes a dot if there’s a note and four characters behind it, or another dot right behind it. Each dot depends only on what’s already on screen.

What it recognizes

205
key prefixes, from 82 issuers
691
ways of saying a secret follows

The prefixes are the start of a key itself, like ghp_ or sk-ant-. The rest are the words around a secret, whatever it looks like: GITHUB_TOKEN=, "password": ", Authorization: Bearer , --api-key=.

AI25

  • Anthropicsk-ant-api03-qp8YJHVARCfcAGRVedv7d5hkgJTs
  • Anthropicsk-ant-admin01-MM46JaUT5xNhkLuR3Cb9crDkWbwA
  • Anthropicsk-ant-api01-C74XU2Uv66e48EDhzCBmpAGRpP7P
  • Anthropicsk-ant-oat01-mFJMN5hpf3L8jVner5yFiSnS2AEv
  • Anthropicsk-ant-3WpPC9FnPCanDZLcDKLbKPcKy7v6
  • OpenAIsk-proj-PR4vsBYEABFWEqrWyEPv8CruzDHk
  • OpenAIsk-svcacct-6rLbdrCUBEZ4q7jHKSPiKne6pePN
  • OpenAIsk-admin-3fhWQQM7XcsHa76WzpPXL6fpinex
  • OpenAIT3BlbkFJBHDeusjuiFa4uNPfjGrDm8FcuNNr
  • OpenAIsk-proj-fedramp-EChfecqBwNijUm7MSiM86kt8k3kB
  • OpenAIsk-svcacct-fedramp-RhHSf7gGVU29AySZFHqxARSKMLCd
  • GoogleAIzaGWnze9m7UJtgsAhZkXtwL6cbLKSJ
  • GoogleAQ.HKw6CVsT7tGGCHKMTzrqiuxZMFZr
  • Hugging Facehf_m5AYPnnGvZst8DAQfTTdXg9iQmrm
  • Hugging Faceapi_org_2YrgwKXxuWFZGxqJinuTAFa6csjh
  • Replicater8_eTCzSr8ZwcwKGgJJMBnx8Vv4DzwZ
  • Perplexitypplx-6MteBymfXdVYZcbfAbqA6yUmBBYh
  • Groqgsk_zVRYd5wLLiMvBpv9CjzhyPyanN84
  • xAIxai-AmkFVY9UUKp3Up22LWypMWwpqP6i
  • OpenRoutersk-or-v1-180e0e04cefb14ed18afd98c9097
  • Together AItgp_v1_jhMrPmWdyGuZLDCg9WuneEZHKzJM
  • Fireworks AIfw_4tQ29LL5hVTmw3GjRDHc9sEhZy6b
  • DeepSeeksk-d692ecbbed2f7f163cf1cae58e9b
  • ElevenLabssk_e35e95260ebeaec0f6a0b014643b
  • Pineconepcsk_kPY7dctCxKfAh7qhCvwVFCMM4hPe
  • LangSmithlsv2_pt_5506676bc83c
  • LangSmithlsv2_sk_5c1a5154fee9
  • LangSmithlsv2_tr_bedb402bff4d
  • Langfusesk-lf-fruZmUfJv9GpGGQZyeRbMCLwUmhW
  • Heliconesk-helicone-jwzJwu2KpYfA5bfjAFyJwtPvr5c7
  • Cursorcrsr_Ef67PgEstyJNGQBrR7tvxfUcGxjm
  • Sourcegraphsgp_iuXfa7pyZUgRRgMU2GV5rYs8Zc9w
  • NVIDIAnvapi-7KPEX2yMTsUr8dv74wzRC6x9DAzX
  • Cerebrascsk-a0d46888923c
  • Voyage AIpa-N9NCY6C9LcAPxxXtAyi79cbA8RfR
  • Tavilytvly-dWS6MTcUDiZZuLQxBa28CEQkbeuV
  • Firecrawlfc-VUiFQSJgipp2SXLxSieu4REmidvm
  • Anyscaleaph0_ytZENFpYW7uwEutuvqB8p76c8uyA

Cloud17

  • AWSAKIAZBCE3YZFTLJZKB2JWAF653TBXN5K
  • AWSABSKQmVkcm9ja0FQSUtleSzVSayxSDYHg6icPgdqRW4GjVw7yW
  • AWSbedrock-api-key-YmVkcm9jay5hbWF6b25hd3MuY29tdDJBuXu6MQqNq7Tz3wer2HN2Gqek
  • AWSASIAU6KYPMVE4A2ZF6FAGJLYX6XKRB42
  • AWSABIA3XNMQTEBJZVWX5ZVPCSJMWD3RNSK
  • AWSACCAWCUCELDVRVN7CXSP5PDRQMLSRGPD
  • Cloudflarecfk_kLEEZU6RNHBMuwfwexksbZhqSTYc
  • Cloudflarecfut_HtQgKsUC7HABK9wVvFAdZkgCJMfT
  • Cloudflarecfat_hCvU2Cm2gxuNgDiHjYQgQqz6rYwn
  • Cloudflarecfast_DqRpTnwwDtkqqa8Gbgmdbynxi73G
  • Cloudflarecfoat_vMndXpBeABhyEt5t4CcSECK9gaes
  • Vercelvck_9uASKchzNa88kBPbJm6CVGGEDcAP
  • Vercelvcp_dL6hHGiXzcwB3QFzefDqQMh3Wkj9
  • Vercelvca_WXwwTJtHa7EhU9zHL7UYkVyWzq55
  • Vercelvcr_rxeBTs7SQArjAzu29Xcrs2b22rZP
  • DigitalOceandop_v1_3838ad059d77612e0dcd0e9bd908
  • DigitalOceandoo_v1_ef26956704a9cdf17a47c67fa1cc
  • DigitalOceandor_v1_d54f966bb2341d7b267d2a9b93fb
  • Dopplerdp.st.Tr9bR6eVpGJqUYAquTJMrfWuiiuT
  • Dopplerdp.pt.242eedb12d62
  • Dopplerdp.ct.6emM3ssKTq4huwTqdUGXT6MipY62
  • Dopplerdp.sa.wPQ6Eeh3DAL83XKLS3q8pip2aAF9
  • Dopplerdp.said.nv7vAheMihSD9yPwvtLewDJ4jNcZ
  • Dopplerdp.scim.kgcWqCKqLPShvySLcxPJMZ2hB8mz
  • Dopplerdp.audit.Mrs4Zk67wY3whJcyHHtRLGTgFeWj
  • Supabasesb_secret_vRvpjtQ6kvAdFPf7RKUAxM3zaFxB
  • Supabasesbp_PKdnMHYJTRMhg28MeUagi4ZSRZYn
  • Databricksdapid78defe25fa2f671be42956b675e
  • HerokuHRKU-u5k8HXAtKwjRNKURxX9ne6qshdYL
  • PlanetScalepscale_tkn_DyScK5LecYveENTFrYLs9fpvpPfx
  • PlanetScalepscale_pw_rKhFNgcG4hj3SKu5jKnXJRvUcauS
  • PlanetScalepscale_oauth_8DjyezwxdNQKQBFWkEpLqzpSMS9s
  • PlanetScalepscale_app_secret_AnkyyJin5ieYZDApJeWbdwq5En9A
  • 1Passwordops_eyJjgpWpwMiAvuYfHB8QGBq22ErC7C3
  • Google CloudGOCSPX-U8x72eJkzk99XXDCLauubyPZWVsz
  • Google Cloudya29.HMQN6u9bvcWeg5kGncFiA6mDSAKe
  • Entra IDQ~AzFTdrmAi86PAubeYG8Fg2mq2jvG
  • HashiCorphvs.M4weQ2vTCDfDGuYVy54Yr8nD38q3
  • HashiCorp.atlasv1.KBgLBuhLf6NcUKHhvarWafK6EkSu
  • HashiCorphvb.cvBHgPrQgEKFvbZawXG8HXdvNDex
  • HashiCorphvr.2vNmUuXF6LrXNCfRKxQmQGm2Ujs6
  • Pulumipul-89721d2ffc86753a3a9cb7c71685
  • Dockerdckr_pat_BxTBchpnurEeGXxMgTFiLh4sLD5N
  • Dockerdckr_oat_be9E8EKaAtphig9AY6TTjyzv6VnB
  • Netlifynfp_TeX4t9RVxDEGE6kmNb5mXhTpsRnh
  • Netlifynfc_pU88Kn7a6hypwBdEmiS2gUp4KATZ
  • Netlifynfo_XiAzZNXqZyKXrEU3Fv3Mh7q8RJcc
  • Netlifynfu_KPJhnFfnNn5YpFkHJDarzGTcFyDb
  • Netlifynfb_f3cgNntgYCftDir7RrquL8v8YAy2
  • Fly.iofm2_Y2DGYW8qZniMyf5gRvAHnapMBkJU
  • Fly.iofm1r_hgiLPVSDvuzX6APrAAwWSjmyFn2P
  • Fly.iofm1a_brdRgfrTATUgrSkKitBy3gqZRiRZ
  • Fly.iofo1_sNj3WPzLPmGeKCTZ3sWDT5hCuYCV

Code & CI9

  • GitHubghp_MKWLwVVmkAVWCADvjy6hx6FVavtb
  • GitHubgithub_pat_tsaLXTeiiSiHwiAkAewuvEnGjmWK
  • GitHubgho_Jn5km4HUzJZF365pfXpdf2qc65Bc
  • GitHubghu_yLfp4a82QMtLMwKRwQzhvCFnREVf
  • GitHubghs_3WgEEkH58KAMKuKpfwaaJqYpdpNa
  • GitHubghr_2SCNJTPnp4xTEGjiuXfQKmtLBH45
  • GitLabglpat-w8KUVftKdtX7JeLJVwgLXqzG5vZX
  • GitLabgloas-PSAF3hErWKAbBqiJEgurCHDfZqUs
  • GitLabgldt-WvBHysUE9dJnhc5aC82mx8RWdeNn
  • GitLabglrt-8tjshbHYUap6TMnYyxAXE6scnWtj
  • GitLabglrtr-vGAk7agFYZHyTHJzVgXnn6GhjFPk
  • GitLabglcbt-zY9VyVasGhVpW5jxA7MU4YvQe9CE
  • GitLabglptt-B3GYFTCv8jyZrM3dSahhhJxGKhFn
  • GitLabglft-Cm9Y7CdwFncD53EE9EEsaAHLrFVy
  • GitLabglimt-D8rizheuX7yBRXuTiKeqM9wpTeDs
  • GitLabglagent-pAGCmVAtbb3tjQy3hNApv27eWqJh
  • GitLabglwt-az6tjhXMK8nEF75PvxpQB95HjiCM
  • GitLabglsoat-ZemTdQhVW73X8TSMMc5GzSGasPeH
  • GitLabglffct-2G8CpVaDQVQ82CJ4vTxbppXKjsjv
  • GitLab_gitlab_session=3SFnUqdVCQDjaRW4vKF9B9MPmksA
  • Linearlin_api_EWZfuitFMJxMCKgZPsxYDAYwD24g
  • Linearlin_oauth_dM7BjfdaytEKv6sFGDimMMbi3r9T
  • PostmanPMAK-ydSWMvHnvdz4zYRkNN7Bd88dvstQ
  • SonarQubesqu_3555fc09e3b64d85a1498f145e55
  • SonarQubesqa_b8f2ab6b143926c7b6c95ec74d8f
  • SonarQubesqp_575ad6dfa5f38123ed9a2024a293
  • CircleCICCIPAT_JVW6vGVhxsg2ZvpuqFisati5pbTS
  • CircleCICCIPRJ_BCKVCT2zpDQjaTd4QrPerxRGqQDZ
  • Buildkitebkua_QCKZeJw8CEqjfcusa6z7MxRTvy78
  • Buildkitebktx_ukP7JsK8vxUP9DVqLTkZQWePhgY6
  • Buildkitebkct_zLT3cKrQGRbTvXmv4h7L8P2abnRG
  • Buildkitebkar_rfijsX6v4upMyEUpUwzedaabtEKY
  • Buildkitebkjat_davWERmF4BZA97cYDpcSfNADkUbv
  • Buildkitebkaa_SXzwPGihasHPZYk9fv65GMdeYuGc
  • Buildkitebkaj_RZmy9gch7m5tkRDX8qXNLtQgUb4V
  • Buildkitebkpt_tgSFAqVEK5cUBVwwKzGnEV32dwz2
  • Buildkitebkpat_zxDxEXkz7MBW9mUFc5Li4cKCn7Pp
  • Buildkitebkps_kHS39HviuezceTHXXdFfetMF2Ugs
  • AtlassianATATT3xFfGF0LtqpBbBnqu7GkivWTe82796jq8n5
  • AtlassianATCTT3xFfGN0tTEEzKc44j5aLweR9PiEMewTfL3j
  • Figmafigd_npSvKYXNL9jtgW2Uwu65CMx6rmUS

Payments5

  • Stripesk_live_rvp47Xy7ECrdLwrvTk6LYifNMQCd
  • Striperk_live_vqDGek8SMwh4VB4s2nZCQtSBiw9N
  • Stripesk_test_qDjrrh4myWpHhpfrSzefAPzSrdbN
  • Striperk_test_xD9SH79udiwJTZEvsYjTyYjpPEG7
  • Stripesk_org_MXPyQ3HccH45gCFC7DwBJ7C5dWef
  • Stripewhsec_7EY9DpqzkqpqXXeHzcqKebSDJjY5
  • Squaresq0csp-Sy4KaRgef332pf576ECk8c6T4vPA
  • Squaresq0csb-iDSkHAFBxqJcGmTFkvpMsTLDkdbb
  • Squaresq0atp-BSsTFAcjQSynqRnSnLyM3rTWtKPr
  • SquareEAAAYRUiqgJXSYePvYxAGvVuQdu4DhKb
  • Shopifyshpat_ed88cc1c964bac5c46be283a054a
  • Shopifyshpca_e6a4d1d479170777bf8ad31f4bbf
  • Shopifyshppa_96668cef66dbcc762755200d434d
  • Shopifyshpss_c2eeba6db37c0be72c14c68c1f8f
  • BitcoinxprvG6za8vgh77S63KzJFANM3W7P8Qgf
  • BitcointprvR7UeaeudnsbJEax32adPRKnC6aVF
  • Plaidaccess-production-6VpnqZeB94H98ynBXzVtcNyDJqnK
  • Plaidaccess-development-m5AaeLvges22BuGKq8RCbUEKTrmx
  • Plaidaccess-sandbox-4W42mnfPSqAPFy9WeSyKyBHsAJDs

Messaging5

  • Slackxoxb-GJNuaxysMvG8sG5EUJihRGiUWKMS
  • Slackxoxp-5eMqfnvBWppZ3tp8xwPvwGrHJyRD
  • Slackxapp-e7Q9Qkgks77aN6GhbYEnPWbg3Bz9
  • Slackxwfp-r7E2A4v5TVUeZTjNcSQi8NBec3e8
  • Slackxoxe.xapp-MZwurBkzRhN2WMqF3zfghQ2khQuE
  • Slackxoxe.xoxb-UNNiHSZTfHc8DstHJHUsG9p3rfFK
  • Slackxoxe.xoxp-g5XcKp7YzwXXsHimbGnXtTmTXUpy
  • Slackxoxe-PPWi8MQWmKw7sHN7bim6NzDpfsaV
  • Slackhooks.slack.com/services/c54SA4LAfUuEQnkuuxduGFMENBZL
  • Slackxoxa-B23yrUzzvXMs6x6Bqr8UPGN5SHba
  • Slackxoxr-rUrcY2JiEh2Kd4NTqu6PdWbAbGxh
  • Slackxoxo-DhyYje7Pwf5gjgiKNEBEtrX7bWpf
  • Slackxoxs-FHpX9cGxpT9Kt75VeENf8HR5cYGX
  • Slackhooks.slack.com/workflows/PbUP4g573hZHUN368pvHsL3rmWGY
  • Slackhooks.slack.com/triggers/Zm84G3fHicTHDrVckWZd2tPHh7ib
  • SendGridSG.26QBjcN2XWcpnvPyp7rjLyiJQUiiqhUJTMsYna3aYUTEjtNx8i
  • Discorddiscord.com/api/webhooks/Aqfiey6DCFYp3iP27C8rKzeLJYm3
  • Discorddiscordapp.com/api/webhooks/3Yy4Csd54FGz8Yb8xq9GEPWwrtyt
  • Telegram232719:AA4zUaAcyyZWFn2RzSmW3aEZFrMdTs
  • Resendre_r6ay1LG7_cgppyJRKeVWrMusNXQAXPk5thfa66A9MEMCLJ

Registries7

  • npmnpm_TbkxW6Qypy5fx79ExvJSJngXs5jC
  • PyPIpypi-AgEdpdKMp5BYc4qRdy3EK78ZSgE8nWW
  • RubyGemsrubygems_d9949af883a72f6a1df401c76097
  • NuGetoy2twilunzjbzeeyso6yaqpr5ssydbs
  • crates.iocio_tp_fyvk2K9SBXTjnXnxJBK7shBBxupF
  • ClojarsCLOJARS_c8eecc30250bbf68de773207d82f
  • JFrogcmVmdGtumkN9ZNXNFGZKBLfag4MK7azaQ9xV

Data & ops10

  • Notionntn_194309c54b47
  • Sentrysntrys_JRxQmY3JBV8EFWTXvqwsLK5icfUT
  • Sentrysntryu_3102118cc118c8e82fba42aa3edb
  • Grafanaglsa_XVQG4GDeWwZXRu5TFDjnPYY8Tpy2
  • Grafanaglc_37HSARceqT5hQMyVGzvFA4TXCB7T
  • Datadogddpat_VRY6aBqmHsPJutXFiYqSR7i8qceR
  • Datadogddsat_Bfu8FbiaDFUQkygpxyaKvAVkiRV5
  • Datadogddapp_NpiCgXvE6WD3mkWGb8NCwNThSfbV
  • New RelicNRAK-GPY9ywHQJm4XtGpJ24hTxradT6ik
  • New RelicNRII-7q3H4Q3gzrWwh2rMRDm35x3ENStn
  • PostHogphx_NjPuhuQi8AHLKP2McTvdvKzEAXa9
  • PostHogphs_p2Dx7pRMhyEtgWtWsPM4NHpUQgg2
  • PostHogpha_GWmEaabyrnPchCx5PFvURq3jf5pv
  • PostHogphr_QXKnJPn8PfbZF6yEi7WRHeZNXY4v
  • HubSpotpat-na1-JKgEHQ88kYEQmgPw4B5VaVbk8d3d
  • HubSpotpat-eu1-MxB8AmnHjqVe7XNN7rdFbpmBrfbu
  • AirtablepatXHnDNLNyRVTeXv.3755697a85bf8158178eeef2abc7cb232f2cda038c1
  • ContentfulCFPAT-2BNtzzPzT69fytvpbDfZLEtsE37W
  • Typeformtfp_w4mvZyDkuVpTw76Gy9rUezfarKWp

Standards4

  • IETFeyJ7gmbps6wZmej3ydSQkABJqUnez4J
  • ageAGE-SECRET-KEY-1B444YR3U7767MDWZZTCENEHFHHSY
  • ageAGE-SECRET-KEY-PQ-1N7AVBWV77X6GDMUGHTW53QJFLJHT
  • PEM in base64LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tuJL6tuCTSKmwr5nv3fpMZg8HGQpn
  • PEM in base64LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tg82G3wVFFceXBqyxMJeFCfYNR9hL
  • PEM in base64LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tfbqjNs4p6B8MhUiFYNRsFQeEKt7T
  • PEM in base64LS0tLS1CRUdJTiBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tzD6wtXjz8QFQHzUykCTAziFPUdXJ
  • OpenSSHb3BlbnNzaC1rZXktdjEzYL9UNerzaCjsf67pjexYHJzjXNY

The words around a key

  • Env variableTOKEN=dPLpM4rZXSDFLUf7MfreiGhUphbF
  • Env variableSECRET=cKeZwMxfDAqYjNFycH5REwy3abdm
  • Env variablePASSWORD=W4vsNLJfyRgTvNAfyFpMCDMJEyiP
  • Env variablePASSWD=TXkLqwL58u6CyTqiCRpvrW3xJYqu
  • Env variableKEY=7jpcymxA3QGqvnBDA2HeyAWzbGH8
  • Env variableAPI_KEY=Qvftnf35yB9XdpdStDrxD97SmGmJ
  • Env variableAPIKEY=9muUR4A8DN9cSVSMVcjq8D8jg9N7
  • Env variableACCESS_KEY=pebBjmDeNeW8gCj2wcRfZ6yZMXsg
  • Env variableSECRET_KEY=EzK9kRtJDJ4XKzTB3MpiiYFuTdWJ
  • Env variablePRIVATE_KEY=3TvcRyF8JfKPTg7YDKy27xMT5yaJ
  • Env variableACCOUNT_KEY=kUeN4ch9u38vXWScNBENri5LpdzW
  • Env variable_KEY=67UYuDMDvLBYu9DVDnncYubE2BVE
  • Env variablePGPASSWORD=XsmzAgLenAkrQL6qDQiGn9nNvpgU
  • Env variableMYSQL_PWD=54f4hiq7v69R4DXsxxapVUPcBgfb
  • Env variable_authToken=DzAPYFLxwdzr8JwLBFcuxx6uuQXG
  • Env variable_auth=vv9eRfWWEMWMpTYtPWrbfyTS34UA
  • JSON / config"api_key": "DfXtE8h7VFL76snkPR3CAKkHRNMN
  • JSON / config"apiKey": "mYYmD5VzVq7LC7EutjHYxea67aAB
  • JSON / config"apikey": "RYYZAanWPC96Fn8aAubfiCpUDA9C
  • JSON / config"api-key": "46KNaHqBVqcfQneEVzDX2bUYpw4X
  • JSON / config"ApiKey": "GjgHityKtLqFLbpZKNgBcCEacKTq
  • JSON / config"access_key": "DRWkYGnrWkErkXZL43DTqeaAWikT
  • JSON / config"token": "bZ6XqFarUEyaeFXaqT3wzCbBaPJ8
  • JSON / config"Token": "H9yKUsshnKCPPL9AttvcKKY7eHaP
  • JSON / config"private_key_id": "q3fNY9xBC9bgTrhkvugbeM2CBBED
  • JSON / config"client_secret": "4dGwenWsaZLqyRWLtaYPninRDpan
  • JSON / config"clientSecret": "gDQiigk5s2K8m7UEh22fr3rvDaeV
  • JSON / config"refresh_token": "fNrZJLnMa2YyKZt4GS9WxXQhStSS
  • JSON / config"refreshToken": "8NKwtehWPCkkfntK9SEKjU5kL5nQ
  • JSON / config"access_token": "M2MGXrnGhBWVHV2xhftPrf6TR8j5
  • CLI flag--token=VVhhN8WZyCpWESwPhvkdNGNz2BV2
  • CLI flag--password=YacNjgjcBih5aWTtsZLzz2HKKqJm
  • CLI flag--api-key=TTEFingiaiF3Ps4wMpv832TD5Wjp
  • CLI flag--secret=TSFqKy3Pc2URq4Q8abPdhusJWApf
  • CLI flag--auth-token=iBZciPDK57j7FPUrRSNui4FtD7Aj
  • CLI flag--access-token=ULFK5AG9JxSNU5d4EGJfXrm5sYxV
  • CLI flag--token 7qVXuWehZccZfFQ8EF6A87MBrPXc
  • CLI flag--password HyxJzxRXqg5bKYvXRD6L7r7Z7kYf
  • CLI flag--api-key 4PqxkHuxU7Q7MN9bDitz3SLrqZgT
  • CLI flag--secret wz3Hb47wSNmS5gJbYSP65GpAN2tA
  • CLI flag--auth-token xv85XcMNWn3HHy8psgMdhctmM455
  • CLI flag--access-token d66ae6fb542c
  • HTTP headerAuthorization: Bearer yLDUdjUTaqd8SETxfi24zXmv3utJ
  • HTTP headerauthorization: Bearer xG9Ubzzxu8JSaSgkPWCcGd7Z6gsq
  • HTTP headerauthorization: bearer ifh8SmQHbuhc8pRV8PMJ2nWBAbKe
  • HTTP headerAUTHORIZATION: Bearer 7J3YNTBpXaFVFaDAmiAPuuBNXNxH
  • HTTP headerAuthorization: Basic Q65qkkfzWdKsvN9hQ9jE7PG9Sp9g
  • HTTP headerauthorization: Basic v7yQtg3qiYAAx5vE5SGK2B335vFZ
  • HTTP headerAuthorization:Bearer XveBhGtNHu2JDenZad9pj4MjRCNq
  • HTTP headerauthorization:Bearer JgenzJSxLXpcHs7kUeVcsAew8LZk
  • HTTP headerAuthorization:Basic TgegwJNiqtkTBqnWVwVLkCTCAyqk
  • HTTP headerAuthorization: Bot bg3eTsxvVD3FjsK5PjmsveQ9Ncer
  • HTTP headerauthorization: Bot pVpRAukRY2e77qYTFX9wpYg7ndxM
  • HTTP headerAuthorization: SSWS DUjiAJiVzP6P2XKAsds6VYTidfqf
  • HTTP headerauthorization: SSWS MGUetw7n77NK4Gciku62GpJF99qX
  • HTTP headerX-Postmark-Server-Token: ChsdmDSaHkSAuJhdJxaePTzCYeH4
  • HTTP headerX-Postmark-Account-Token: tp57jQyrb6E69ZQ3DyLKDauWntMw
  • HTTP headerAuthorization: 5it655WYexUXEH4YJjwNk89BngKU
  • HTTP headerauthorization: zDp9sSSEwRNKPdcGrCYD5jS6pWAG
  • ConnectionAccountKey=UAq4tzpTbWeWvq9r6xPvgrHsuQPV
  • ConnectionSharedAccessKey=RVKb2ypqcd74UhuWPN7sTyuTNQgP
  • ConnectionPassword=5QCY2S6nW2r8QCzrMdhFbKkKgae8
  • ConnectionPwd=jbE9msW57c7qBhtEbu2mzZQF7H6f
  • YAML / envTOKEN: be5a0cefc205
  • YAML / envSECRET: aWT89uhuEj7gkWb24MajBDSz3JwF
  • YAML / envPASSWORD: HUbbradZ3qxbzfUXPzUFbJjWYDLz
  • YAML / envPASSWD: cWEmUA9jCXsGLTzn4UaiRBWbvPPx
  • YAML / envAPI_KEY: JV88PLyjJeyPCvKiXHfLniDJgkF4
  • YAML / envAPIKEY: quUdLPvAkp6nJc4tStq2i8zcJne6
  • Config itemTOKEN"] = "7MMVuQhRpmRrsZTGGBtzw8i2MBVx
  • Config itemSECRET"] = "UaCfFgJdm77QiN89yQSBGd7ErXjN
  • Config itemPASSWORD"] = "mLKxs7yfA2BBp4ZEy2ZSkELeBCSu
  • Config itemPASSWD"] = "R9xt3KuGpdwEKP3VBEWCmZpAivuw
  • Config itemAPI_KEY"] = "JJBMCrmf6jPKHb6r62Ba7Ldjx3q3
  • Config itemAPIKEY"] = "sFrLGrGTySz8gYEfmnmv2xWZK2jN
  • JavaScriptapiKey: "XArjeS5pF3vwyabvzkEecfWHUq8R
  • JavaScriptapi_key: "aea8vUiQsHXeUVKJhkswBqCRE243
  • JavaScriptapikey: "EYQw7cqMVx2sNg4KNHWRvXEBgGdg
  • JavaScriptapiSecret: "FLwN5GvvFDxZp5D7r5Xkcf5RnnGE
  • JavaScriptclientSecret: "tLb3CHNrNG8kv5Yjbp8KsXRhK7xt
  • JavaScriptclient_secret: "sk2jUrFB2cWFi2fXgPTi5pSeCkJ3
  • JavaScript_client_secret: "4rnZ93NSxCkLskWd44ugQVLkVbiu
  • JavaScript_secret_key: "PNcDpDaj5WGDffTMT3hNkRwarS2U
  • JavaScript_access_token: "w9JPzgb2RXTAAQZThuPz9DfBz8sF
  • JavaScript_auth_token: "7wapDYujGFnwa9VY6C5Qg4Wk6B5p
  • JavaScript_refresh_token: "bsqkjPyZBuVVGQBNuMiRa69LAPMb
  • JavaScript_password: "wWE3VxHdteyNHNZBYTa8djLTKpcj
  • JavaScripttoken: "L2McfkPmDhd45cUCX9xVe7DA45iQ
  • Credentialsaws_session_token = JTSZ647PZE6BDTGN
  • Credentialsaws_secret_access_key = z8PLHtzGPGtPdC8iyUwZnUA4UJ9S
  • Credentialsclient_secret = dniJJmWTyz3phPVrjpSJGYeCNjLk
  • Credentialsprivate_key_id = CPdUnDHeuUE7VeJzrBFBwfD2KYT4
  • Credentialsrefresh_token = wHRSXSQMcqyEEJvLFqEgz2Sgv5PB
  • Credentialsaccess_token = CL8JCywvC64ATVuqt2SuUfiFYjWt
  • YAMLpassword: mM9WF5LNgixYQzTBBGFUReZvXi2u
  • YAMLpasswd: uXfCgU9LcfzhMjbkS4hq2hMVxRuw
  • YAMLsecret: treQ3Fs8LWuL7EzyzST3zfNFg9bz
  • YAMLapi_key: jFK8CXKRjHSuwZTKWZWpjvvFzSbP
  • YAMLapikey: APnV5eSyUgh3CPs4uG78MZ3kPCfY
  • URL query?api_key=bNRc3pPau2wzPGBD7P4WHNq3VUCs
  • URL query&api_key=QaP55KKru3WyDWfUqxBEUTaF4P28
  • URL query?apikey=Q53jewL6jPS7PBEeEKFpuEKj6dm9
  • URL query&apikey=5gRsy9tAgTh4KHwSCzFu4Y8dL6LS
  • URL query?access_token=2qT36cDR3Nun3TUrRihDbHFnEf6Z
  • URL query&access_token=LbWPbkZTmYwPMiDJbbUpKTySamvS
  • PEM keyBEGIN PRIVATE KEY-----MNttJGC3ft9pUWKMcdhghU3cmaf3
  • PEM keyBEGIN RSA PRIVATE KEY-----b9cMBpa6YK4BLMa9aj3N5kbYPXCg
  • PEM keyBEGIN EC PRIVATE KEY-----4DwdMBAhC44255qEiYUsKhng8kvL
  • PEM keyBEGIN DSA PRIVATE KEY-----59PNVcAejVLtEJ8S32JVJ6gZgn8p
  • PEM keyBEGIN OPENSSH PRIVATE KEY-----fPYkMM8J6jThECtpn9w29bvqrb5G
  • PEM keyBEGIN ENCRYPTED PRIVATE KEY-----27qPY3ZDip9uAJwsxd9NsMNQxXwe
  • URL passwordpostgres://admin:YkS6b7xVCWaktXZA@db.internal:5432
Fake keys, live text, set in Obel Mono Masked. Copy a line and paste it anywhere: the whole key comes back.

Knowing when not to

A font that hid your code would be worse than none, so most of the craft is in the restraint. Code is full of names that begin like a key. If the window looks like an ordinary word, nothing happens. Public keys are left alone on purpose. And a mask ends at the first character that couldn’t be in a key, so it never swallows the rest of the line.

Hidden

ghp_aB3dEFGHiJ7kLmN0pQrAPI_KEY=xY7kQ2pLm9ZtR4vBAuthorization: Bearer aZ81kPq0xT7vLm3N

Left alone

ghp_importMONKEY=bananaSORT_KEY=modified_datepk_live_51HxT7vLm3NaZ81kPq0
Live text, both columns set in Obel Mono Masked.

Why a dot

It’s the mark a password field draws, and it looks like nothing you’d find in a key. An asterisk would suggest the key contains asterisks. So the dot is larger and lower than a bullet, and solid where o, 0 and ° are hollow.

periodbulletozerodegreeasteriskthe mask

What it can’t do

It changes pixels, not text. That’s the deal, and it’s worth being clear about.

Hides it from

  • A screen share
  • A recording or a stream
  • A screenshot in a bug report
  • An agent that reads your screen

Doesn’t hide it from

  • Copy and paste
  • The file on disk
  • Your shell history
  • Any program that reads the text

It also works one line at a time. If a key wraps onto a second line, there’s nothing behind it on that line to go on, so it shows. And if a key has already been seen, rotate it. No font can take that back.

Turn it on

Obel Mono Masked is always on, for apps with no font settings, like iTerm2, where you also turn on Use ligatures. It’s meant for Terminal.app and Warp too, but we haven’t tested those yet. Obel Mono has it as a switch, ss01, for apps that let you set one, like Ghostty, VS Code and Zed. The download page has the setting for each app.

To check, run this in your terminal. With masking on, you’ll see dots:

printf 'OBEL_TOKEN=masked-when-on\n'

Apps that don’t shape text at all, like Alacritty, can’t run any font rules, so they show keys as typed.

For the curious: the actual rules

These are chained contextual substitutions in OpenType’s GSUB table, with a backtrack context only. Simplified, the rules for ghp_ read:

# the note: the _ that ends GitHub's prefix
sub g h p underscore' by underscore.sec.alnum.lower;

# the dots: a note and four characters behind, or a dot behind
sub underscore.sec.alnum.lower @alnum @alnum @alnum @alnum
    @alnum' by secret.mask.alnum;
sub secret.mask.alnum @alnum' by secret.mask.alnum;

The quote mark sits on the glyph being replaced; everything before it has already been drawn. The real rules are generated from a data file and compiled into the font.

In Obel Mono Masked they live in ccmp, which every shaping renderer applies and no editor setting turns off. In Obel Mono they live under ss01.

Before release, the rules ran through the three engines that shape nearly all text on screen, HarfBuzz, CoreText and DirectWrite, on thousands of keys and near-misses. All three drew the same glyphs.

I make things like this. I’ll share the next one on at @codycollier.