<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: TiltedLunar123</title>
    <description>The latest articles on DEV Community by TiltedLunar123 (@tiltedlunar123).</description>
    <link>https://dev.to/tiltedlunar123</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3847611%2F5372ff69-df32-4335-9ef6-65d8c9504ae5.jpeg</url>
      <title>DEV Community: TiltedLunar123</title>
      <link>https://dev.to/tiltedlunar123</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tiltedlunar123"/>
    <language>en</language>
    <item>
      <title>Security+ Incident Response: The Order Is the Question</title>
      <dc:creator>TiltedLunar123</dc:creator>
      <pubDate>Thu, 12 Nov 2026 10:52:41 +0000</pubDate>
      <link>https://dev.to/tiltedlunar123/security-incident-response-the-order-is-the-question-5gne</link>
      <guid>https://dev.to/tiltedlunar123/security-incident-response-the-order-is-the-question-5gne</guid>
      <description>&lt;p&gt;Most Security+ questions about incident response don't test your understanding of what each phase is. Instead, they present a scenario and ask what should come next. If you have the steps memorized, the correct answer should logically fit into the situation. But if you just know the definitions, a few options might seem plausible, leaving you to guess.&lt;/p&gt;

&lt;p&gt;Objective 4.8 outlines the process in seven steps: preparation, detection, analysis, containment, eradication, recovery, and lessons learned. NIST SP 800-61 groups these into four phases, folding containment, eradication and recovery (the action steps) into one. Know both versions; either could be used, but the seven-step list helps spot the tricky options.&lt;/p&gt;

&lt;h2&gt;
  
  
  Find where the story is
&lt;/h2&gt;

&lt;p&gt;Before you look at the options, read the situation and ask one thing: what has already happened? If the alert just went off and no one knows if it's real, you're in the detection or analysis phase. If the team has confirmed the malware and knows which hosts are affected, analysis is done and the next step is containment. Once the hosts are cut off and the attacker can't get back in, eradication is next.&lt;/p&gt;

&lt;p&gt;The habit pays off because the distractors are nearly always real steps. They're just the wrong move for this moment. Picture a confirmed infection that is still spreading. The question will offer "reimage the servers" and "restore from last night's backup" beside "isolate the hosts." All three occur at some point. Only one takes place now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contain before you clean
&lt;/h2&gt;

&lt;p&gt;The trap CompTIA leans on most is rushing to eradication. Cleaning up malware seems like the right action, but doing so can backfire. If the system is still connected to the network and the attacker retains access, a freshly cleaned machine will simply be reinfected, and you'll have destroyed evidence you might need later.&lt;/p&gt;

&lt;p&gt;Containment, on the other hand, restricts the damage. Short-term containment is swift and can be reversed; disconnect the host from the network, block the attacker's IP or domain at the firewall, and disable the compromised account. Long-term containment keeps things secure while you plan the recovery, such as moving systems to a separate VLAN and changing passwords.&lt;/p&gt;

&lt;p&gt;So, when a question says the threat is confirmed and still active, look for the response that halts its spread. That is the answer CompTIA seeks, even if a seemingly more comprehensive option is presented.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clean before you restore
&lt;/h2&gt;

&lt;p&gt;The second trap is rushing into recovery. Restoring from backup looks like the end goal, and it is, just not while the underlying issue is still present. If you restore while the vulnerability that was exploited remains, the attacker can easily return through the same entry point.&lt;/p&gt;

&lt;p&gt;To truly eradicate the problem, you must remove the cause. This means not just deleting the obvious infected file but also eliminating any methods the attacker used to maintain access, like scheduled tasks or services. You also need to patch whatever allowed the attacker in and invalidate any credentials or tokens they might have stolen. Recovery then restores from a backup taken before the breach and checks that the system is clean before it goes back into production. After that, you watch it closely for a while.&lt;/p&gt;

&lt;p&gt;A related concern is deciding which backup to use. The most recent one is often the wrong choice. If the compromise began before that backup, restoring it will reintroduce the malware. The proper choice is a backup from a time before the breach that has been confirmed to be clean.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two ends people mix up
&lt;/h2&gt;

&lt;p&gt;Preparation and lessons learned can seem alike because they both relate to planning. The key difference is the timing. Preparation takes place before any incident happens. That's where playbooks get written, tools like EDR and SIEM get set up, and backups get tested. Lessons learned happen after an incident, as a review of what was slow and what caused it, leading to action items that each have an owner.&lt;/p&gt;

&lt;p&gt;The two concepts connect, which is why NIST illustrates the lifecycle as a loop. Findings from lessons learned feed back into preparation. If a question describes updating a playbook because of what went wrong in the last incident, CompTIA refers to that as lessons learned. A question about writing the playbook before anything has happened is preparation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tabletop or simulation
&lt;/h2&gt;

&lt;p&gt;The same objective also deals with testing the plan, and the main issue is determining which method is suitable. A tabletop exercise involves a discussion where the team goes over a scenario, and nothing gets touched, so it is a cheap and safe way to find gaps in roles and escalation. A simulation, on the other hand, introduces realistic events into an environment resembling production to test detection and hands-on response using real tools. If the question asks to test decisions without putting production systems at risk, then it is a tabletop exercise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practicing it
&lt;/h2&gt;

&lt;p&gt;The way to master this is by shifting focus from memorizing the list to placing scenarios on it. Pick any incident question and, before looking at the options, state aloud which step the story is on and which one follows next. After repeating this a few dozen times, the wrong answers begin to seem clearly too early or too late.&lt;/p&gt;

&lt;p&gt;When I was preparing for Security+, scenario questions were my weak spot until I started doing this. The &lt;a href="https://secplusmastery.com/r/ch-devto?to=/security-plus-practice-questions" rel="noopener noreferrer"&gt;free practice questions&lt;/a&gt; include incident response scenarios, the &lt;a href="https://secplusmastery.com/r/ch-devto?to=/security-plus-objectives" rel="noopener noreferrer"&gt;objectives page&lt;/a&gt; shows where 4.8 is located in Domain 4, and if you're aiming for the practical side of the exam, the &lt;a href="https://secplusmastery.com/r/ch-devto?to=/security-plus-pbq" rel="noopener noreferrer"&gt;free PBQ&lt;/a&gt; requires no account.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://secplusmastery.com/blog/security-plus-incident-response-order" rel="noopener noreferrer"&gt;secplusmastery.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>cybersecurity</category>
      <category>career</category>
      <category>learning</category>
    </item>
    <item>
      <title>SAML vs OAuth vs OIDC on Security+: Pick by the Question</title>
      <dc:creator>TiltedLunar123</dc:creator>
      <pubDate>Mon, 09 Nov 2026 19:10:05 +0000</pubDate>
      <link>https://dev.to/tiltedlunar123/saml-vs-oauth-vs-oidc-on-security-pick-by-the-question-91d</link>
      <guid>https://dev.to/tiltedlunar123/saml-vs-oauth-vs-oidc-on-security-pick-by-the-question-91d</guid>
      <description>&lt;p&gt;Objective 4.6 on SY0-701 mentions LDAP, OAuth, and SAML as single sign-on standards. The exam rarely tests your ability to define them. Instead, it presents a login scenario, an app integration, or a partner portal, and asks which standard fits. The wrong answers are typically the other names from that same group. Understanding what each acronym is used for is more important than knowing its definition.&lt;/p&gt;

&lt;h2&gt;
  
  
  One question per protocol
&lt;/h2&gt;

&lt;p&gt;Each of these systems was created to solve a distinct problem, and that's the main idea.&lt;/p&gt;

&lt;p&gt;SAML addresses "who is this person, and can I trust the company that says so?" An identity provider signs the user in and then presents the app with a signed XML message known as an assertion. The application relies on the signature rather than seeing the password. Picture an employee who opens Salesforce or Workday and lands on the corporate sign-in page before the app itself.&lt;/p&gt;

&lt;p&gt;OAuth 2.0 deals with "what is this app allowed to do on my behalf?" You grant a calendar app permission to access your schedule, and in return the app gets an access token that's limited in scope. The app can view your calendar but not your email, and it won't discover your password. Note what OAuth omits: it doesn't reveal your identity to the app. OAuth is focused on authorization, and the exam frequently highlights this difference.&lt;/p&gt;

&lt;p&gt;OpenID Connect (often written as OIDC) is the piece that bridges that gap. It works on top of OAuth 2.0 and introduces an ID token, a signed JWT that identifies the user. When a website offers "Sign in with Google", that button is OIDC in action atop OAuth. OIDC isn't listed in the 4.6 list, but you need it to understand the challenge further down.&lt;/p&gt;

&lt;p&gt;LDAP is the outlier. It's a directory protocol for reading and writing the user, group, and attribute storage behind the login. Often, the identity provider checks a password against LDAP, rather than using it to log into an app. Basic LDAP sends bind credentials in plain text, so the secure version is LDAPS or LDAP with StartTLS.&lt;/p&gt;

&lt;h2&gt;
  
  
  The scenario words that decide it
&lt;/h2&gt;

&lt;p&gt;Read the stem for the use case before checking the options. A few phrases keep repeating.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Employees sign in once to several cloud apps through the company identity provider" refers to SAML.&lt;/li&gt;
&lt;li&gt;"Let a third-party app access a user's data without giving it the password" refers to OAuth.&lt;/li&gt;
&lt;li&gt;"The app needs to know who the user is", or a "sign in with" button for a social account, refers to OIDC.&lt;/li&gt;
&lt;li&gt;"Query the directory for group membership" refers to LDAP.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the stem mentions both "access" and "without sharing the password", it is OAuth most of the time. If it includes "assertion" or "XML", it is SAML, because no other option uses those terms.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trap: OAuth used as a login
&lt;/h2&gt;

&lt;p&gt;The most common mistake in this objective is choosing OAuth when the topic is really about identity. OAuth seems correct because the Google sign-in button appears to use OAuth, and it does involve an OAuth flow. However, an access token just indicates the app is allowed to perform certain actions; it doesn't reveal who the user is. Apps that rely on access tokens for user verification can be deceived into accepting a token intended for another application. That's precisely why OIDC was developed.&lt;/p&gt;

&lt;p&gt;A simple distinction keeps them separate: OAuth authorizes, OIDC identifies. For situations where the app needs to know the user's identity, the correct answer is OIDC, or SAML in a business setting. If the app only requires permission to access data on behalf of a user, then OAuth is the right choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Federation is the bigger picture
&lt;/h2&gt;

&lt;p&gt;A lot of 4.6 questions don't focus on a single protocol. They talk about two organizations. For instance, if a partner company's staff need access to your project portal, but you don't want to create or manage accounts for them, that's federation, a trust relationship where their people sign in at their own identity provider and your app accepts the result. SAML or OIDC handles the login across that trust.&lt;/p&gt;

&lt;p&gt;If the question also mentions that access should end when someone leaves the partner company, the answer typically includes SCIM, which pushes account changes from their directory to your app automatically. Creating guest accounts manually is a common distractor, as people rarely remember to delete them.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to drill this
&lt;/h2&gt;

&lt;p&gt;Run through the four protocols and put each one's question into your own words. SAML is about who this person is, OAuth is about what this app can do, OIDC adds who the user is on top of OAuth, and LDAP is about what the directory says. After that, work through 4.6 questions in one sitting so the four names stop running together. CompTIA's own wording for 4.6 is on the &lt;a href="https://secplusmastery.com/r/ch-devto?to=/security-plus-objectives" rel="noopener noreferrer"&gt;full objectives list&lt;/a&gt;, the &lt;a href="https://secplusmastery.com/r/ch-devto?to=/security-plus-practice-questions" rel="noopener noreferrer"&gt;practice questions page&lt;/a&gt; is a good place to try the use-case reading, and each acronym here has its own entry in the &lt;a href="https://secplusmastery.com/r/ch-devto?to=/glossary" rel="noopener noreferrer"&gt;glossary&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://secplusmastery.com/blog/security-plus-saml-oauth-oidc" rel="noopener noreferrer"&gt;secplusmastery.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>cybersecurity</category>
      <category>career</category>
      <category>learning</category>
    </item>
    <item>
      <title>Gmail cleanup rules are about categories. Your opinions are about senders.</title>
      <dc:creator>TiltedLunar123</dc:creator>
      <pubDate>Sun, 01 Nov 2026 17:12:08 +0000</pubDate>
      <link>https://dev.to/tiltedlunar123/gmail-cleanup-rules-are-about-categories-your-opinions-are-about-senders-210b</link>
      <guid>https://dev.to/tiltedlunar123/gmail-cleanup-rules-are-about-categories-your-opinions-are-about-senders-210b</guid>
      <description>&lt;p&gt;Much of the Gmail cleanup guidance begins with a category. Remove all emails in Promotions over a year old. Empty Social. Dispose of forum digests. It helps, but then it does something you didn't intend, because a category is only Gmail's assumption about an email and says nothing about how you feel about it.&lt;/p&gt;

&lt;p&gt;Think about your actual feelings toward your mail. You don't really care about the "Promotions" category. Instead, you have strong views on one airline that emails you four times a week, on a shop where you're waiting for a return, and on a newsletter you read every Sunday even though Gmail buries it next to the coupon messages. So, your opinions are about specific senders, while the rules apply to categories, and that disconnect is where the cleanup goes wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where category rules misfire
&lt;/h2&gt;

&lt;p&gt;A rule like &lt;code&gt;category:promotions older_than:1y&lt;/code&gt; handles all senders in the same way within that tab. So the receipt from a store ends up in Trash with the sale emails from the same store, as Gmail sorts them together under the same address. The newsletter you enjoy receives the same fate as the ones you ignore. Meanwhile, the sender who emails you every single day gets treated just like someone who wrote to you twice.&lt;/p&gt;

&lt;p&gt;The rule does what it says. The issue arises when it gives an answer to "which emails are old and promotional?" while the question you're really interested in is "who do I never want to hear from again?"&lt;/p&gt;

&lt;h2&gt;
  
  
  The sender is the unit you can decide on
&lt;/h2&gt;

&lt;p&gt;When you examine each sender individually, the choices become straightforward, as there are just four options.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep.&lt;/strong&gt; You either read it or might need it later, so best to leave it be.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Archive.&lt;/strong&gt; You want it removed from the inbox but still accessible. Important to know: archiving doesn't free up storage space; it's more about tidying. Look elsewhere if saving space is the aim.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Delete.&lt;/strong&gt; This is the only one of the four that frees space, and only after the mail has left Trash.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unsubscribe.&lt;/strong&gt; This stops the next email from arriving. It doesn't delete anything you've already received, so it usually goes along with Delete.&lt;/p&gt;

&lt;p&gt;That is the entire menu (and yes, it really is just four options). Choosing from four choices for one sender is a much smaller decision than crafting a rule that must fit for a few hundred senders all at once, and you can make it in just a few seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing it by hand in Gmail
&lt;/h2&gt;

&lt;p&gt;You can accomplish this right now using just Gmail's search function.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open a recent email from a sender you suspect is mostly noise, and search &lt;code&gt;from:&lt;/code&gt; with their address. Now you see all their emails to you in one list.&lt;/li&gt;
&lt;li&gt;Look at the total count and scan the subjects. That should give you a good idea. If receipts are mixed in, narrow it down, for example &lt;code&gt;from:shop@example.com -subject:order&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Make your decision. Select all, then use "Select all conversations that match this search" if there are multiple pages. Archive or delete.&lt;/li&gt;
&lt;li&gt;If the choice is to delete, open one of their emails and use Gmail's unsubscribe option at the top, so you won't see future emails from them.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Any single sender takes a minute. The tricky part is figuring out which ones to check first. The ones that really count are the ones that send lots of emails, and Gmail doesn't have a feature that organizes your inbox by sender, so most folks rely on their memory and overlook the biggest culprits.&lt;/p&gt;

&lt;h2&gt;
  
  
  A few rules that keep this safe
&lt;/h2&gt;

&lt;p&gt;Whatever tool you choose, these are worth having:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Move mail to Trash before anything gets erased for good. You get thirty days there to catch a bad call.&lt;/li&gt;
&lt;li&gt;Leave starred, important, unread and labeled mail out of it unless you really mean to include it. Each of those is a signal you already gave Gmail that a message matters.&lt;/li&gt;
&lt;li&gt;When you aren't sure, go one sender at a time. Undoing one bad sender decision takes seconds, while a bad rule run over the whole inbox can take an evening to sort out.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where my extension fits
&lt;/h2&gt;

&lt;p&gt;I developed a Chrome extension named Gmail One-Click Cleaner, and it includes a Sender Triage feature that operates like this: it displays senders one by one, and you choose one from four options. Deleted emails go to the Trash first, and each run gets labeled so you can restore them with a single click. By default it also skips the mail you've already marked as mattering (starred, important, unread or labeled). The free version of the extension processes three senders per run, while the paid version handles up to 25.&lt;/p&gt;

&lt;p&gt;If you prefer to stick with Gmail, the manual process described earlier achieves the same result, just more slowly. No matter your approach, the key change is this: instead of wondering about the email's category, consider what you think of the sender.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://chromewebstore.google.com/detail/bmcfpljakkpcbinhgiahncpcbhmihgpc" rel="noopener noreferrer"&gt;https://chromewebstore.google.com/detail/bmcfpljakkpcbinhgiahncpcbhmihgpc&lt;/a&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Grading piano timing in the browser with Web MIDI</title>
      <dc:creator>TiltedLunar123</dc:creator>
      <pubDate>Fri, 02 Oct 2026 15:25:47 +0000</pubDate>
      <link>https://dev.to/tiltedlunar123/grading-piano-timing-in-the-browser-with-web-midi-90g</link>
      <guid>https://dev.to/tiltedlunar123/grading-piano-timing-in-the-browser-with-web-midi-90g</guid>
      <description>&lt;p&gt;I picked up piano again this year and wanted to connect my digital piano to my laptop, read real sheet music, and figure out which notes or beats I missed. But none of the apps I tested could do that. So I created &lt;a href="https://keystair.com" rel="noopener noreferrer"&gt;Keystair&lt;/a&gt;, a browser-based piano course that listens to a USB MIDI keyboard. Most of it is standard React code. A few browser details took me longer than they should have, so here they are.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading the keyboard
&lt;/h2&gt;

&lt;p&gt;Web MIDI is a simple API where you request access once, attach handlers to each input, and read three bytes per message. The tricky part is handling the note-off signal; many keyboards skip it and send a note-on with a velocity of 0 instead, so that has to count as releasing the key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;access&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requestMIDIAccess&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;sysex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;access&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmidimessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0xf0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mh"&gt;0x90&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mh"&gt;0x80&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Plenty of keyboards send note-on with velocity 0 instead of note-off.&lt;/span&gt;
      &lt;span class="nf"&gt;onNote&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;pitch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mh"&gt;0x90&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;t&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeStamp&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mh"&gt;0xb0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Control change 64 is the sustain pedal.&lt;/span&gt;
      &lt;span class="nf"&gt;onPedal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeStamp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The sustain pedal appears as control change 64, meaning it's down if its value is 64 or higher. The event's &lt;code&gt;timeStamp&lt;/code&gt; runs on the same clock as &lt;code&gt;performance.now()&lt;/code&gt;, which matters later on. Output works in reverse: &lt;code&gt;MIDIOutput.send()&lt;/code&gt; takes a timestamp too, so the app can play an example at a precise moment on the student's own piano.&lt;/p&gt;

&lt;p&gt;Permissions are worth mentioning briefly. If &lt;code&gt;navigator.permissions.query({ name: 'midi' })&lt;/code&gt; indicates that the site already has access, the app reconnects on the next visit without prompting. It also monitors this permission; if someone allows MIDI later via the address bar, they get connected immediately, no reload needed. Chrome, Edge, and Opera have Web MIDI built in, while Firefox requires accepting an add-on prompt. Safari and the browsers on iPhone and iPad don't support it, so there the app falls back to using the microphone.&lt;/p&gt;

&lt;h2&gt;
  
  
  One clock for everything
&lt;/h2&gt;

&lt;p&gt;The metronome click comes from the Web Audio API, with a clock maintained by an &lt;code&gt;AudioContext&lt;/code&gt;: &lt;code&gt;currentTime&lt;/code&gt;, in seconds, with no fixed tie to &lt;code&gt;performance.now()&lt;/code&gt;. If you schedule clicks based on one clock but judge key presses on another, each grade will be off by whatever delay the speakers introduce. I keep all times in &lt;code&gt;performance.now()&lt;/code&gt; milliseconds and convert only when scheduling sounds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Audio clock time (s) at which a sound is heard, for a performance.now() time (ms).&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;toCtx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AudioContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tPerf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getOutputTimestamp&lt;/span&gt;&lt;span class="p"&gt;?.();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;contextTime&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;performanceTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contextTime&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tPerf&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;performanceTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// Fallback: the current time, minus the latency the browser reports.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputLatency&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseLatency&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tPerf&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;lag&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;getOutputTimestamp()&lt;/code&gt; aligns the two clocks at the moment a sample actually plays through the speakers, so a click meant for beat 3 is heard right then, not slightly later. Not every browser supports it, which is why there's a fallback.&lt;/p&gt;

&lt;h2&gt;
  
  
  How close counts as on time
&lt;/h2&gt;

&lt;p&gt;A beginner playing at 60 beats per minute and someone playing eighth notes at 120 can't share the same time window; these windows are determined by the tempo. A note counts if it's within half a beat, up to 400 milliseconds. It's considered on time within 18% of a beat, kept between 70 and 140 milliseconds. Faster notes reduce both, so one note's window never stretches into the next one's. At 60 BPM, that means 400 ms and 140 ms; for eighth notes at 120 BPM, the windows are 125 ms and 75 ms.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// stepBeats is the shortest gap between two notes in the piece, in beats.&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;timingWindows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msPerBeat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stepBeats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;step&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stepBeats&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;windowMs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;msPerBeat&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;step&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;goodMs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;140&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;msPerBeat&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.18&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;windowMs&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;goodMs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;windowMs&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each key press goes to the nearest unmatched note of the same pitch within its time frame, and anything left over counts as an extra note. A run passes if 90% of the notes are played and 70% are on time, allowing for about one extra note per ten notes. Afterward, the app highlights the bars where you rushed or dragged in both color and words, because "early in bar 3" is easier to act on than a red note head.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measuring the delay
&lt;/h2&gt;

&lt;p&gt;Every setup introduces a delay from pressing a key to the browser recognizing it, with speakers adding even more time. So rather than guess, the settings have a timing check where you tap along with 12 clicks at 80 BPM. The first four are only a count-in, so they're ignored. For the remaining eight, the app finds the closest tap to each click, measures the gap, and saves the median as an offset that later runs subtract. I chose the median so that one missed or double tap wouldn't skew the result.&lt;/p&gt;

&lt;h2&gt;
  
  
  The key press that doesn't count
&lt;/h2&gt;

&lt;p&gt;This can be easily overlooked: browsers don't start an &lt;code&gt;AudioContext&lt;/code&gt; until they receive a user gesture, like a mouse click or keyboard key press, but MIDI notes don't count. If the page just loaded and the only thing the student interacted with was the piano, the context remains suspended, and &lt;code&gt;resume()&lt;/code&gt; keeps waiting for a click that doesn't come. No errors appear; there's just no sound.&lt;/p&gt;

&lt;p&gt;So the start waits on &lt;code&gt;resume()&lt;/code&gt; with a 400 ms timer next to it. If the audio hasn't started by the time the timer goes off, a little message asks the student to click or tap somewhere to turn the sound on. Then once the sound begins, this message disappears.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The first click or key press anywhere on the page lets the audio start.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wake&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pointerdown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;wake&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;keydown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;wake&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pointerdown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;wake&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;keydown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;wake&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Before a timed run: wait for the audio, and ask for a click if it's stuck.&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;running&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;running&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;showSoundNotice&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Still blocked. wake() will start it on the next click or key press.&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;statechange&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;running&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;hideSoundNotice&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Moving tunes to new keys
&lt;/h2&gt;

&lt;p&gt;In later lessons, students play the same melodies in different keys. Moving a MIDI note a few semitones is simple; what's important for someone reading the staff is how these notes are spelled: an E in C major turns into F# in D major, and it should never end up as Gb. Each note shifts by a number of semitones and a number of letter names at once, with the sharp or flat worked out from the difference:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LETTERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CDEFGAB&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;NATURAL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// semitones above C for each letter&lt;/span&gt;

&lt;span class="c1"&gt;// E4 moved up 2 semitones and 1 letter is F#4, never Gb4.&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;transposeName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;semitones&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;([&lt;/span&gt;&lt;span class="sr"&gt;A-G&lt;/span&gt;&lt;span class="se"&gt;])(&lt;/span&gt;&lt;span class="sr"&gt;#|b&lt;/span&gt;&lt;span class="se"&gt;)?(&lt;/span&gt;&lt;span class="sr"&gt;-&lt;/span&gt;&lt;span class="se"&gt;?\d)&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Bad pitch name: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pitch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pitchFromName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;semitones&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// MIDI number, C4 = 60&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;li&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(((&lt;/span&gt;&lt;span class="nx"&gt;LETTERS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;acc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(((&lt;/span&gt;&lt;span class="nx"&gt;pitch&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;NATURAL&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;acc&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;acc&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; would need a double sharp or flat`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;octave&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pitch&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;NATURAL&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;LETTERS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="p"&gt;]}${&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;acc&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;b&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;octave&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A note that would need a double sharp or double flat throws an error. The transposed melodies are built when the content module loads, so a bad key choice fails the tests instead of confusing students with unexpected notes.&lt;/p&gt;

&lt;p&gt;Keystair is available at &lt;a href="https://keystair.com" rel="noopener noreferrer"&gt;keystair.com&lt;/a&gt;. Stage 1 is free and you can test it out without signing up, in either English or Spanish. If you've worked on a project using Web MIDI, I'm curious how you dealt with latency.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>showdev</category>
      <category>music</category>
    </item>
    <item>
      <title>Security+ Port Questions Want the Secure Swap</title>
      <dc:creator>TiltedLunar123</dc:creator>
      <pubDate>Thu, 24 Sep 2026 09:32:44 +0000</pubDate>
      <link>https://dev.to/tiltedlunar123/security-port-questions-want-the-secure-swap-4e7p</link>
      <guid>https://dev.to/tiltedlunar123/security-port-questions-want-the-secure-swap-4e7p</guid>
      <description>&lt;p&gt;Plenty of people create a deck of forty port numbers to memorize and still struggle with the port questions on the SY0-701 exam. The deck works well for its purpose, but the test doesn't usually ask just for the number. Instead, it might mention a protocol that transmits data in plain text and ask what should be used instead, or show a firewall rule and ask what's wrong with it.&lt;/p&gt;

&lt;p&gt;That's objective 4.5, where secure protocol selection sits right next to firewall rules. So the thing worth learning is the swap: the insecure protocol, the secure counterpart that replaces it, and which port each one uses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn the pairs, not the list
&lt;/h2&gt;

&lt;p&gt;A simple list treats all numbers equally, which can make them hard to remember. With pairs, each number has a partner that helps explain it. You should know these swaps well.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Telnet uses port 23 and switches to SSH on port 22.&lt;/li&gt;
&lt;li&gt;HTTP runs on port 80 and changes to HTTPS on port 443.&lt;/li&gt;
&lt;li&gt;FTP operates on ports 20 and 21, and can switch to SFTP on port 22 or FTPS on ports 989 and 990.&lt;/li&gt;
&lt;li&gt;LDAP works on port 389 and moves to LDAPS on port 636.&lt;/li&gt;
&lt;li&gt;POP3 uses port 110 and changes to POP3S on port 995; similarly, IMAP on port 143 becomes IMAPS on port 993.&lt;/li&gt;
&lt;li&gt;SMTP runs on port 25 and can switch to submission on port 587 with STARTTLS, or to SMTPS on port 465 with TLS from the start.&lt;/li&gt;
&lt;li&gt;Syslog sends on port 514 and moves to syslog over TLS on port 6514.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once the pair is the unit, many questions become clear. An admin handling switch management via Telnet should use SSH. A directory lookup that travels across the network in plain text should use LDAPS. You're finishing a pair you already know half of, which is much easier than trying to find one number out of forty.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two traps where the fix isn't a new port
&lt;/h2&gt;

&lt;p&gt;Most swaps lead to another port, making it simple to think they all do. However, two of the most tested ones don't, and the incorrect answers are built on that misunderstanding.&lt;/p&gt;

&lt;p&gt;The first is SNMP. In versions 1 and 2c, community strings travel in plain text over UDP ports 161 and 162. SNMPv3 remedies this by incorporating authentication and encryption, still using those same two ports. So an option to switch SNMP to a secure port is incorrect. The solution lies in upgrading the version.&lt;/p&gt;

&lt;p&gt;The second is DNS, with a twist. Traditional DNS on port 53 lacks encryption, meaning anyone along the route can see your queries. DNSSEC, which sounds like it might help, only signs DNS records to detect fake responses; it doesn't encrypt anything, so lookups remain visible. Encrypting the actual query is done by DNS over TLS (DoT) on port 853 or DNS over HTTPS (DoH) on port 443. If a question asks how to prevent others from seeing which sites a user looks up, DoT or DoH is the answer. To stop fake responses, DNSSEC is the solution. Both are part of DNS security but address different issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  SFTP and FTPS are not the same thing
&lt;/h2&gt;

&lt;p&gt;Both are known as secure FTP, and the exam leans on that confusion. SFTP works within SSH for file transfer and isn't related to the FTP protocol, so it uses port 22, which is also used by SSH and SCP. FTPS is standard FTP encrypted with TLS, and it operates on ports 989 and 990.&lt;/p&gt;

&lt;p&gt;That difference raises a real question. In a scenario where the firewall only allows port 22 outbound, SFTP will function but FTPS won't. If the organization desires one open port for remote shell and file transfer, the choice is SSH, as SFTP and SCP use that port too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where ports show up in a firewall rule
&lt;/h2&gt;

&lt;p&gt;You'll find port numbers in rule tables, often in a performance-based question. The task here is to look at each rule and figure out if that particular traffic should pass through.&lt;/p&gt;

&lt;p&gt;A few rules stand out. An inbound Telnet allow on port 23 from any source is a problem because it's unencrypted. Allowing Remote Desktop Protocol (RDP) on port 3389 from the internet is an issue too, and this one often catches people out. Even though RDP is encrypted, RDP exposed to the internet is a leading entry point for ransomware. Just being encrypted doesn't mean it's safe to expose. The rule table is testing exposure, not only encryption.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to drill it
&lt;/h2&gt;

&lt;p&gt;Write down the insecure part of each pair first, then add the secure counterpart and its port number. Next, reverse the order. Begin with the secure protocol and explain what it replaced. Finally, say out loud what each secure version adds, whether that's encryption, authentication, or integrity, because that detail is what separates DNSSEC from DoT.&lt;/p&gt;

&lt;p&gt;Once that's solid, read ports inside real rules instead of on their own. A &lt;a href="https://secplusmastery.com/r/ch-devto" rel="noopener noreferrer"&gt;free firewall rule question&lt;/a&gt; will tell you quickly if the pairs hold up when mixed with source addresses and directions. The &lt;a href="https://secplusmastery.com/r/ch-devto?to=/security-plus-ports" rel="noopener noreferrer"&gt;full port table&lt;/a&gt;, including less common ports, is worth a single pass once the pairs are solid. CompTIA's wording for objective 4.5 is listed in the &lt;a href="https://secplusmastery.com/r/ch-devto?to=/security-plus-objectives" rel="noopener noreferrer"&gt;objectives&lt;/a&gt; as well.&lt;/p&gt;

&lt;p&gt;When a question mentions a cleartext protocol, start by looking for its paired protocol first. If the question names SNMP or DNS, pause and verify whether the solution involves a newer version of the protocol or an alternative one before proceeding.&lt;/p&gt;

</description>
      <category>security</category>
      <category>cybersecurity</category>
      <category>career</category>
      <category>learning</category>
    </item>
    <item>
      <title>The Security+ Log Question Has One Right Source</title>
      <dc:creator>TiltedLunar123</dc:creator>
      <pubDate>Tue, 22 Sep 2026 09:33:41 +0000</pubDate>
      <link>https://dev.to/tiltedlunar123/the-security-log-question-has-one-right-source-2of</link>
      <guid>https://dev.to/tiltedlunar123/the-security-log-question-has-one-right-source-2of</guid>
      <description>&lt;p&gt;Domain 4 is the largest part of SY0-701, and a fair portion of it is one type of question dressed up differently. A short scenario outlines what an analyst needs to figure out, and then four data sources are listed below it, and you pick one. Firewall logs. Endpoint logs. DNS query logs. Vulnerability scan results.&lt;/p&gt;

&lt;p&gt;Most study material prepares you for the wrong version of that question. It teaches the log types as a list, detailing the fields each one contains, and quizzes you on what a firewall log includes. The exam rarely tests that. Instead, it asks &lt;strong&gt;which source answers the question in the scenario&lt;/strong&gt;, and the best way to prepare is to learn the mapping from the question to the source rather than the contents of each source.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three layers, three questions
&lt;/h2&gt;

&lt;p&gt;The layer a source resides at tells you what it can prove, and that's really the key.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The boundary proves reachability.&lt;/strong&gt; Firewall logs list source and destination IP addresses. They also include port, protocol, and the allow or deny decision, making them quick for spotting denied inbound traffic, port scans, and failed egress. A wall of denies from one internal host is a scanning or beaconing signature. What can a firewall log not tell you? What ran on the machine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The host proves execution.&lt;/strong&gt; Endpoint and EDR logs record process creation with the parent-child relationship intact, which is how you see that Word spawned PowerShell. They also track file system and registry changes and link an outbound connection to the exact binary that opened it. When the scenario asks about what ran, or asks you to confirm a parent process, you're dealing with the host.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The application proves what was touched.&lt;/strong&gt; Application logs connect activity to a user ID and a session ID and a request ID and an API endpoint. That follows one actor's path through the software and shows which records got queried. Nothing at the network boundary can answer that.&lt;/p&gt;

&lt;h2&gt;
  
  
  The distractor is always the source that might have a clue
&lt;/h2&gt;

&lt;p&gt;Question creators often use nearby sources as wrong answers. The cleanest example is packet capture against flow records. Both are network evidence, both would be in the room during an investigation, and they answer different questions.&lt;/p&gt;

&lt;p&gt;Flow records show who talked to whom and how much and for how long. They're cheap to keep and are the backbone of network investigation, which is why they appear safe. Packet capture is the full content of the traffic at one point in the network, and it's the only source that reveals what was actually transferred. So if the scenario asks about data leaving the environment or what a session contained, you want the packet capture, even though the flow record looks relevant.&lt;/p&gt;

&lt;h2&gt;
  
  
  An alert and a block are different pieces of evidence
&lt;/h2&gt;

&lt;p&gt;Intrusion detection and intrusion prevention are often taught together, with a slash in between. On this objective, the slash matters quite a lot. An &lt;strong&gt;IDS log shows an alert.&lt;/strong&gt; An &lt;strong&gt;IPS log shows a block.&lt;/strong&gt; Both provide a signature ID and the offending packets.&lt;/p&gt;

&lt;p&gt;Why does this distinction matter? Because it is evidentiary rather than trivia. If the question is whether the attack hit its target, an IDS entry says the traffic was seen and allowed through, and an IPS entry says the session was stopped. Same signature, opposite conclusion about impact. A question asking about containment would look for the block.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two sources that describe state rather than activity
&lt;/h2&gt;

&lt;p&gt;Vulnerability scan results and configuration and change data are the other reliable distractor pair, and they fail for one reason. They describe what a system looks like rather than what happened on it. A scan says port 3389 is open and unpatched. It doesn't say anyone connected. Configuration and change records show what the device was set to and when someone made changes, which is useful for proving unauthorized changes but not for reconstructing an intrusion.&lt;/p&gt;

&lt;p&gt;Dashboards fit into the same caution. A dashboard summarizes volume so a person can spot patterns, and it's a starting point rather than evidence. Every finding on one gets confirmed against underlying logs before it goes into a report, and an option that offers a dashboard as proof is almost always wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to drill this
&lt;/h2&gt;

&lt;p&gt;Read the stem's verb first before looking at the options. Who logged in? That's authentication, so it goes to the OS security log, which means Windows Security or &lt;code&gt;auth.log&lt;/code&gt; depending on the platform. What ran? That's execution, so it goes to endpoint logging. What was transmitted? That's content, so it's packet capture. What was reachable? That's about the boundary, so it's the firewall. There are only four verbs and they cover a lot.&lt;/p&gt;

&lt;p&gt;Then practice the objective as a block rather than scattered through a mixed set, because the sources only separate when you see them side by side. The &lt;a href="https://secplusmastery.com/security-plus-domain-4-practice" rel="noopener noreferrer"&gt;Domain 4 practice set&lt;/a&gt; is filtered to security operations, and objective 4.9 is listed as Use Data Sources to Support an Investigation in the &lt;a href="https://secplusmastery.com/security-plus-objectives" rel="noopener noreferrer"&gt;full objectives list&lt;/a&gt; if you want CompTIA's own wording. Individual source names sit in the &lt;a href="https://secplusmastery.com/glossary" rel="noopener noreferrer"&gt;glossary&lt;/a&gt; as well.&lt;/p&gt;

&lt;p&gt;One final habit. When two options both look defensible, ask which one directly answers the question and which one just contains a clue. That's the split the question was built on. Once you spot it, the four options stop being four.&lt;/p&gt;

</description>
      <category>security</category>
      <category>cybersecurity</category>
      <category>career</category>
      <category>learning</category>
    </item>
    <item>
      <title>Every Security+ Control Answer Needs Two Labels</title>
      <dc:creator>TiltedLunar123</dc:creator>
      <pubDate>Sun, 20 Sep 2026 09:37:43 +0000</pubDate>
      <link>https://dev.to/tiltedlunar123/every-security-control-answer-needs-two-labels-4m99</link>
      <guid>https://dev.to/tiltedlunar123/every-security-control-answer-needs-two-labels-4m99</guid>
      <description>&lt;p&gt;Objective 1.1 seems to focus on vocabulary. There are four categories and six types to memorize, and then you progress. Then, an exam scenario about a badge reader or a written policy asks which control it is, and half the group picks something the other half has already dismissed.&lt;/p&gt;

&lt;p&gt;Why do people disagree on that? Because the question demands two labels, while most study materials present everything as a single list of ten terms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two axes, not one list
&lt;/h2&gt;

&lt;p&gt;A control belongs to a category and a type. Categories are about who or what enforces it. There are four:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Technical.&lt;/strong&gt; A system does it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Managerial.&lt;/strong&gt; Someone decides and writes it down.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operational.&lt;/strong&gt; People do it as part of their job.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Physical.&lt;/strong&gt; You can walk into it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The type is about timing. It's where the control fits relative to the potential problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Preventive&lt;/strong&gt; stops it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterrent&lt;/strong&gt; makes someone decide not to try.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detective&lt;/strong&gt; notices it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Corrective&lt;/strong&gt; cleans it up afterward.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Directive&lt;/strong&gt; tells someone what to do.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compensating&lt;/strong&gt; replaces a control that can't be used.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every control has one label from each side. A fence is physical and deterrent. Encryption at rest is technical and preventive. A quarterly access review is managerial and detective. Practice answering with both words even when the question asks for just one, as the second word often clarifies the first.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same control can move between boxes
&lt;/h2&gt;

&lt;p&gt;This is where it feels unfair. A security guard is always physical. The type changes with whatever the question says the guard is doing.&lt;/p&gt;

&lt;p&gt;A guard standing at the entrance where everyone can see him, in a question about people deciding not to walk in, is a deterrent. A guard checking badges and turning people away is preventive. A guard watching a camera feed and phoning it in is detective.&lt;/p&gt;

&lt;p&gt;Nothing about the guard changes; the surrounding sentence does, and that's what's being tested. Cameras and logging work the same way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compensating does not mean extra
&lt;/h2&gt;

&lt;p&gt;Compensating is what people grab for when they are unsure, and using it out of uncertainty is usually wrong.&lt;/p&gt;

&lt;p&gt;A compensating control exists when a required control can't be used. The need still stands, though the usual way to meet it is blocked, so something else steps in to cover the same risk. Think of a legacy server that can't handle the current patch; it's isolated on its own network segment with stricter firewall rules and enhanced logging, and that setup compensates for the patch that can't be applied.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What about an extra layer on something that already works? That's just another control, and calling it compensating gets the question wrong. If you can't name the missing control or explain why it's missing, choose a different answer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Directive is the one that gets skipped
&lt;/h2&gt;

&lt;p&gt;A directive control tells someone what to do. An acceptable use policy, a documented procedure for handling customer records, and a sign by the door reading "one person at a time" are all directive.&lt;/p&gt;

&lt;p&gt;The key difference is enforcement. A policy saying laptops must be encrypted is directive and managerial, as it's a policy that controls behavior. Full disk encryption pushed by the MDM that users can't disable is preventive, because it's a technical control that stops something before it happens.&lt;/p&gt;

&lt;p&gt;Both aim at the same goal. They are different types though, and the exam will hand you both in the same question set.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading the question for the right cue
&lt;/h2&gt;

&lt;p&gt;Verbs are key here.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stops it from happening: &lt;strong&gt;preventive&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Makes someone decide not to try: &lt;strong&gt;deterrent&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Notices or alerts or reviews: &lt;strong&gt;detective&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Restores or rebuilds after the fact: &lt;strong&gt;corrective&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Instructs or requires or trains: &lt;strong&gt;directive&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Covers a requirement that can't be met the intended way: &lt;strong&gt;compensating&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The category is often clear. A device or a firewall rule is technical. A policy or an audit cycle is managerial. Awareness training and a process someone runs by hand are operational. Locks and cameras and fences and guards are physical.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Watch out for the word operational; it trips people up. Does it mean a thing that is being operated? No. Operational means humans running a process. A backup job on a schedule is technical because the system runs it. Someone testing the restore every quarter and signing off is operational.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Practice with pairs
&lt;/h2&gt;

&lt;p&gt;The fix is boring, and it works. When you encounter a control in a practice question or a lesson, say both labels before checking the choices. A badge reader: technical and preventive. A warning banner at login: directive, and technical if the system forces you to click it, managerial if it's just in a handbook.&lt;/p&gt;

&lt;p&gt;Do twenty of these and the question stops feeling like vocabulary. Is that tedious? Very. It is also the whole trick. Go through CompTIA's published exam objectives and work down the list instead of relying on someone else's table.&lt;/p&gt;

&lt;p&gt;The drag and match performance-based questions ask you to put controls in the right buckets, too. There's a free one on &lt;a href="https://secplusmastery.com/r/ch-devto" rel="noopener noreferrer"&gt;my site&lt;/a&gt; if you want to see the format before exam day.&lt;/p&gt;

</description>
      <category>security</category>
      <category>cybersecurity</category>
      <category>career</category>
      <category>learning</category>
    </item>
    <item>
      <title>Your Security+ Expires in Three Years. Here Is What Renews It.</title>
      <dc:creator>TiltedLunar123</dc:creator>
      <pubDate>Fri, 18 Sep 2026 10:35:18 +0000</pubDate>
      <link>https://dev.to/tiltedlunar123/your-security-expires-in-three-years-here-is-what-renews-it-4ah5</link>
      <guid>https://dev.to/tiltedlunar123/your-security-expires-in-three-years-here-is-what-renews-it-4ah5</guid>
      <description>&lt;p&gt;Passing Security+ is not the end of paperwork. The certification lasts three years, starting from the day you pass. Renewal costs 50 continuing education units (CEUs) and a fee. Fifty is more than it sounds. Plan for it. Here's what counts toward those 50, what doesn't, and the one route that can finish the whole thing in a single activity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ways to renew with no CEUs
&lt;/h2&gt;

&lt;p&gt;CompTIA lists three ways to renew the certification that don't involve submitting any activities. No tallying and no waiting on a reviewer.&lt;/p&gt;

&lt;p&gt;The first is a CertMaster CE course. CompTIA calls it the fastest option, and Security+ is one of only three certifications it covers, alongside A+ and Network+. The course fee includes the CE fee, which is not nothing. Holders of the higher certifications can't use this route, so renewing SecurityX is harder than renewing Security+.&lt;/p&gt;

&lt;p&gt;The second is passing the newest version of the exam. Sitting the current Security+ again renews it on the spot, which is worth knowing before you book anything else.&lt;/p&gt;

&lt;p&gt;The third is earning a higher-level CompTIA certification. It applies to some certifications and not others. Read your own renewal requirements instead of assuming.&lt;/p&gt;

&lt;p&gt;Everything else? That means earning CEUs and uploading proof of every one of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fifty units, and the rules that decide them
&lt;/h2&gt;

&lt;p&gt;So what counts? Five kinds of activity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;training and higher education&lt;/li&gt;
&lt;li&gt;IT industry participation&lt;/li&gt;
&lt;li&gt;non-CompTIA certifications&lt;/li&gt;
&lt;li&gt;publishing&lt;/li&gt;
&lt;li&gt;related work experience&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Three conditions apply to all five. Miss one, and the activity is worthless.&lt;/p&gt;

&lt;p&gt;Timing. The activity has to happen within your three-year cycle. Something you did before you certified is useless here.&lt;/p&gt;

&lt;p&gt;Relevance. At least half the content must relate to the exam objectives you are renewing against. CompTIA is clear that it looks at both topics and the cognitive level of the material. If you can't point at the objective a course maps to, that's your answer.&lt;/p&gt;

&lt;p&gt;Documentation. Every activity needs paper. Paperwork is where most submissions fail, and it fails them at the last step, after the studying and the money are already spent.&lt;/p&gt;

&lt;h2&gt;
  
  
  The caps are the part people miss
&lt;/h2&gt;

&lt;p&gt;Here's the catch. A live webinar earns one CEU per hour, which sounds good until you read the ceiling. So, how far do webinars get you? Not far at all. They are capped at 10 CEUs for Security+, conferences carry the same cap of 10, and sitting through webinars until the number goes up quietly tops out at a fifth of what you actually need.&lt;/p&gt;

&lt;p&gt;There is no such ceiling on a training course. One CEU per hour, up to the full 50, and a college course is worth 10 CEUs for each three to four credit-hour class, so five classes would cover the whole requirement between them. Teaching or mentoring earns one CEU an hour and caps at 20. Creating instructional materials earns two CEUs an hour and also caps at 20. Sitting in a CompTIA exam development workshop as a subject matter expert has no cap and can cover the full 50 on its own.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There is a shortcut for anyone working under the Department of Defense, and it is the single biggest one on this page for the people it applies to. The DoD Cybersecurity Fundamentals and Cyberspace Operations Fundamentals courses are pre-approved and carry 25 CEUs for Security+. That's half the requirement in one submission. Worth checking before you pay for anything.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  One certification can do the whole job
&lt;/h2&gt;

&lt;p&gt;Were you going to sit another exam anyway? Then a qualifying non-CompTIA certification is the efficient route. CompTIA publishes an approved list per certification, and for Security+, most entries are worth the full 50. These all sit there at 50, which is really the entire renewal in one activity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CISSP&lt;/li&gt;
&lt;li&gt;CISA and CISM&lt;/li&gt;
&lt;li&gt;SSCP&lt;/li&gt;
&lt;li&gt;CEH&lt;/li&gt;
&lt;li&gt;AWS Certified Security Specialty&lt;/li&gt;
&lt;li&gt;the CCNP Security exams&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Read the list. The numbers are not uniform. Microsoft Certified: Security Operations Analyst Associate appears at 38 CEUs, not 50. It is a qualifying certification that still leaves you 12 short.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What gets rejected
&lt;/h2&gt;

&lt;p&gt;So what does CompTIA actually want to see? It is specific about proof, and specific in a way that catches people who did the work but lost the renewal on the evidence.&lt;/p&gt;

&lt;p&gt;For a certification, submit the certificate or the badge link issued by the certifying body. It has to show your name and the exact name of the certification. The date goes on there too. Training completion certificates, score reports, and emails are not valid submissions for that activity.&lt;/p&gt;

&lt;p&gt;For a webinar or a conference session, you need an outline of the content, plus either a completion certificate or the registration email. Your name and the session name have to appear on it, along with the date and the hours. On-demand webinars and YouTube videos count only if you can show proof of registration or completion, which is not something most YouTube watching produces.&lt;/p&gt;

&lt;h2&gt;
  
  
  The SY0-701 wrinkle
&lt;/h2&gt;

&lt;p&gt;One date changes the arithmetic here. It matters most for anyone certified recently. The English SY0-701 exam retires on June 11, 2027. If your three-year window closes after that, the route of passing the newest version means sitting a different exam from the one you passed, against a different set of exam objectives. Nobody needs to panic about that. It is worth deciding early rather than in the last month, because a retake you planned for is a study project and a retake you didn't is a scramble.&lt;/p&gt;

&lt;p&gt;Whichever route you pick, the CE fee applies unless the course price already covers it, and the submission has to be approved before your expiration date rather than on it. Three years feels very long right up until the final quarter of it.&lt;/p&gt;

&lt;p&gt;If the exam route is the one you are heading for, the format is worth re-meeting early. Try a &lt;a href="https://secplusmastery.com/r/ch-devto" rel="noopener noreferrer"&gt;free performance-based question&lt;/a&gt; before you commit to a date.&lt;/p&gt;

</description>
      <category>security</category>
      <category>cybersecurity</category>
      <category>career</category>
      <category>learning</category>
    </item>
    <item>
      <title>A Practice Exam Score Is Not a Security+ Prediction</title>
      <dc:creator>TiltedLunar123</dc:creator>
      <pubDate>Wed, 16 Sep 2026 09:50:13 +0000</pubDate>
      <link>https://dev.to/tiltedlunar123/a-practice-exam-score-is-not-a-security-prediction-28n4</link>
      <guid>https://dev.to/tiltedlunar123/a-practice-exam-score-is-not-a-security-prediction-28n4</guid>
      <description>&lt;p&gt;Every study group has a simple rule: get 85 percent on your practice exams and you'll pass. Some say 90. Others suggest you'll be fine if you score in the 70s or 80s on one vendor's tests. Where does that number come from?&lt;/p&gt;

&lt;p&gt;In August, I took SY0-701 and relied on practice exams, so this isn't against them. The problem is, that rule can't do the job people think it can. The gap between what people think it measures and what it really does measure is where many first-time failures come from.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two kinds of numbers
&lt;/h2&gt;

&lt;p&gt;A practice test gives you a percentage: 68 out of 80 correct, so 85 percent.&lt;/p&gt;

&lt;p&gt;Your Security+ score arrives in a different currency altogether. It's a scaled score, 750 on a scale from 100 to 900, and the lowest score on that scale is well above zero. You can't convert between the two in either direction, because nobody outside CompTIA holds the conversion and there is no fixed one to hold.&lt;/p&gt;

&lt;p&gt;When someone tells you 85 percent means you're ready, they're not giving you a measurement, they're giving you a personal correlation with a sample size of one person.&lt;/p&gt;

&lt;h2&gt;
  
  
  Whose questions were they?
&lt;/h2&gt;

&lt;p&gt;Under that, there's a harder problem. A practice test scores you against a particular set of questions, so what it really shows is how you did on that set.&lt;/p&gt;

&lt;p&gt;Two vendors covering the same objectives can be very different in difficulty, and neither of them is calibrated against the thing you are actually going to sit. One might write items with a clear right answer and three distractors, while another might make you choose between two defensible answers based on a scenario. Score 85 on the first and 65 on the second, and you've found something about the vendors, not yourself.&lt;/p&gt;

&lt;p&gt;The exam has its own difficulty level, set by a process none of those authors are part of.&lt;/p&gt;

&lt;h2&gt;
  
  
  The number that improves for the wrong reason
&lt;/h2&gt;

&lt;p&gt;Here's the one that trips people up. You take a test and score 70. You study. You take it again and score 88, so 18 points better.&lt;/p&gt;

&lt;p&gt;Some of that improvement is real. Some is because you've seen the questions before. That part is not progress.&lt;/p&gt;

&lt;p&gt;Recognizing a question you've seen before is a different skill from working out an answer you have never encountered anywhere, and the exam only ever tests the second of those. A rising score on something you keep retaking is partly about remembering the questions. Each pass shifts more of the score into recall and less into actual competence.&lt;/p&gt;

&lt;p&gt;For the score to matter, the material has to be new every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The thing most tests leave out
&lt;/h2&gt;

&lt;p&gt;Nearly all practice tests are multiple choice. SY0-701 mixes multiple choice with &lt;a href="https://secplusmastery.com/r/ch-devto" rel="noopener noreferrer"&gt;performance-based questions&lt;/a&gt;, and those are tasks. You're given an interface and asked to do something in it. Drilling with four-option questions doesn't train for that, which is why so many say the multiple choice was fine but the simulations weren't. Ask yourself which half you drilled. Where your preparation is a stack of practice tests, the skill you have drilled least is the one most likely to cost you time on the day.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the score is good for
&lt;/h2&gt;

&lt;p&gt;Practice tests aren't a waste. They're still one of the better things to spend money on. They're a diagnostic tool rather than a crystal ball. Three jobs, all of them useful.&lt;/p&gt;

&lt;p&gt;The per-objective breakdown is the real prize, because a result telling you cryptography is sitting at 50 percent stays useful no matter what the overall score said, and unlike the headline number it points at something you can go and repair.&lt;/p&gt;

&lt;p&gt;Timing is the one people often ignore. If you can't finish a set within the time you gave yourself, you've surfaced a problem that will still be there on exam day, long after the score is forgotten.&lt;/p&gt;

&lt;p&gt;Question shape is the quiet one. Getting comfortable with scenarios where two answers both seem right is real preparation, and it's the main argument for doing practice questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  A better readiness test
&lt;/h2&gt;

&lt;p&gt;Want to replace 85 percent and I'm ready? Try this.&lt;/p&gt;

&lt;p&gt;Download CompTIA's exam objectives from the Security+ page behind an email form, not a payment. Work through them line by line, saying out loud what each one means and where you would actually use it, before you let yourself look anything up or check a single answer.&lt;/p&gt;

&lt;p&gt;The lines where you stall are your study list. That test has no scaling, no vendor difficulty, and no seen-question problem because you're not being scored against someone else's writing. You're checking if you can explain the concepts.&lt;/p&gt;

&lt;p&gt;When the stalls end, book it.&lt;/p&gt;

</description>
      <category>security</category>
      <category>cybersecurity</category>
      <category>career</category>
      <category>learning</category>
    </item>
    <item>
      <title>A scheduled delete is a different program from the one you watch</title>
      <dc:creator>TiltedLunar123</dc:creator>
      <pubDate>Tue, 15 Sep 2026 09:43:41 +0000</pubDate>
      <link>https://dev.to/tiltedlunar123/a-scheduled-delete-is-a-different-program-from-the-one-you-watch-3nbn</link>
      <guid>https://dev.to/tiltedlunar123/a-scheduled-delete-is-a-different-program-from-the-one-you-watch-3nbn</guid>
      <description>&lt;p&gt;A cleanup you watch is a different program from a cleanup on a schedule. Same query. Same mailbox. What changes is that nobody is there when the second one runs.&lt;/p&gt;

&lt;p&gt;I added scheduled runs to my Gmail cleaner. Most of that work turned out to have nothing to do with deleting mail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Somebody was catching your bad matches and you never noticed
&lt;/h2&gt;

&lt;p&gt;Run a bulk delete by hand and you watch the count land. Two thousand four hundred, fine. Nineteen thousand, and your hand comes off the mouse before you finish reading the number.&lt;/p&gt;

&lt;p&gt;That pause is a safety control. Nobody wrote it down. It is not in the code anywhere, and it vanishes the moment a run happens at 4am on a Tuesday while the one person who would have flinched at that number is asleep.&lt;/p&gt;

&lt;p&gt;So the first question for a scheduled job is not how to schedule it, it is what the job should do when a result looks wrong. Remember what "looks wrong" used to mean. A human squinting at a number.&lt;/p&gt;

&lt;p&gt;So what replaces the squint?&lt;/p&gt;

&lt;h2&gt;
  
  
  Daily does not mean daily
&lt;/h2&gt;

&lt;p&gt;Browser extensions get no cron daemon. An alarm fires while the browser is running, so your daily job really runs the next time somebody opens Chrome. Work Monday to Friday, shut the laptop on Saturday, and your daily job runs five times a week.&lt;/p&gt;

&lt;p&gt;Now the interesting part. Three runs were missed. Does the job catch up?&lt;/p&gt;

&lt;p&gt;Say it does. A rule that quietly matches a few hundred messages a day now fires against three days at once, and the run that stayed small every time anyone watched it becomes the biggest this mailbox has seen. Say it does not. The mailbox drifts and the tool looks broken.&lt;/p&gt;

&lt;p&gt;Neither answer is free, and picking one is the actual design decision hiding inside a checkbox that says "run this daily". Skipping is the safer default, and it has to be visible, because silent skipping is how somebody ends up believing a cleanup ran all month when it never did.&lt;/p&gt;

&lt;h2&gt;
  
  
  A schedule turns the query into the whole product
&lt;/h2&gt;

&lt;p&gt;An interactive run repairs its own mistakes. You see 19,000, you narrow the filter, you go again. A bad first draft never survives.&lt;/p&gt;

&lt;p&gt;A scheduled run gets no second draft. The query you wrote on Sunday executes against a mailbox that keeps changing under it for months. Somebody subscribes to a newsletter matching your sender pattern. A vendor switches the From address on receipts you meant to keep. The rule did not change. Its blast radius did.&lt;/p&gt;

&lt;p&gt;Which rules survive that? Ones that state their exclusions rather than only their matches. An unattended rule is worth exactly what its exceptions are worth, and those exceptions belong inside the query instead of in the operator's head. At 4am there is no operator.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cap the run, then tell somebody
&lt;/h2&gt;

&lt;p&gt;What makes an unattended delete survivable? Two things.&lt;/p&gt;

&lt;p&gt;First, a per-run ceiling, so a rule that suddenly matches ten times its usual volume hits the ceiling instead of the mailbox. You do not add a cap because you expect the spike, you add it because the entire premise of scheduling is that you are not watching.&lt;/p&gt;

&lt;p&gt;Second, a record you read afterwards: the query, the number of messages actually acted on rather than matched, the time it ran, whatever it skipped and why. A scheduled feature with no report is a rumour that your inbox is being cleaned.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I actually shipped
&lt;/h2&gt;

&lt;p&gt;The extension is Bulk Delete Gmail Emails, also called Gmail One-Click Cleaner: &lt;a href="https://chromewebstore.google.com/detail/bmcfpljakkpcbinhgiahncpcbhmihgpc" rel="noopener noreferrer"&gt;Chrome Web Store listing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Per that listing it runs scheduled cleanups daily, weekly or monthly. A run can be live. It can also be a review pass, or a dry run that touches nothing.&lt;/p&gt;

&lt;p&gt;Those modes are doing the work described above. A dry run answers the "how wrong is this rule" question before the rule is ever trusted alone. Review keeps a person in the loop for the first few passes, which is where you find the exception you forgot about.&lt;/p&gt;

&lt;p&gt;There is a recovery log too, with one-click restore for roughly thirty days. That is the backstop for a run nobody watched, and it ticks along on the same clock Gmail's own Trash uses.&lt;/p&gt;

&lt;h2&gt;
  
  
  The limit, said plainly
&lt;/h2&gt;

&lt;p&gt;A recovery log is a thirty-day window rather than a backup. If a scheduled rule has been quietly eating something you wanted for five weeks, the log cannot help you. Neither can Gmail.&lt;/p&gt;

&lt;p&gt;Which is the argument for aiming a schedule only at mail you have already decided against. Promotional mail from senders you unsubscribed from months ago. Notifications out of a system you left. Categories where the worst case is a shrug.&lt;/p&gt;

&lt;p&gt;The exciting version of this feature is a rule that learns what you ignore and clears it for you. I did not build that one, and I am not going to. Unattended and clever make a bad pair when the failure mode is somebody's mail.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Security+ PBQs Are a Time Problem First</title>
      <dc:creator>TiltedLunar123</dc:creator>
      <pubDate>Sun, 13 Sep 2026 10:14:58 +0000</pubDate>
      <link>https://dev.to/tiltedlunar123/the-security-pbqs-are-a-time-problem-first-3o27</link>
      <guid>https://dev.to/tiltedlunar123/the-security-pbqs-are-a-time-problem-first-3o27</guid>
      <description>&lt;p&gt;Every SY0-701 story has the same shape. Someone walks in prepared, meets a performance-based question early, and then spends the rest of the exam behind the clock. The version I read this week ended in a pass. The line was still in it: ran out of time to re-review anything.&lt;/p&gt;

&lt;p&gt;The issue is arithmetic. The arithmetic is published.&lt;/p&gt;

&lt;h2&gt;
  
  
  The number CompTIA gives you
&lt;/h2&gt;

&lt;p&gt;CompTIA's own exam details page for Security+ says a maximum of 90 questions in 90 minutes, a mix of multiple choice and performance-based, passing at 750 on a scale that runs from 100 to 900.&lt;/p&gt;

&lt;p&gt;So, one minute per question. That is an average and not a budget. A performance-based question (PBQ) is a small piece of work with several steps, and one of those steps is reading an interface you may never have seen before.&lt;/p&gt;

&lt;p&gt;Meet one early and treat it like any other question, and six or seven minutes can go by without you noticing. Do that twice, and you have borrowed a quarter of an hour from questions that would have taken only twenty seconds each.&lt;/p&gt;

&lt;h2&gt;
  
  
  A question you never reached scores zero
&lt;/h2&gt;

&lt;p&gt;The scoring is scaled, so you cannot sit there working out how many you need. What you can do is notice that a question you never reached scores zero. So does one you skipped in a panic on the way past.&lt;/p&gt;

&lt;p&gt;That makes the decision very simple.&lt;/p&gt;

&lt;p&gt;The cheap marks go first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flag it and walk away
&lt;/h2&gt;

&lt;p&gt;Here is the habit worth building before exam day, because you will not invent it under pressure.&lt;/p&gt;

&lt;p&gt;When a PBQ appears, give it one read. Is the shape of the answer obvious? If not, flag it and move on. Work the multiple choice to the end of the exam, banking everything you are sure of. Then come back with two things you did not have on the first pass: the leftover minutes, and an exact count of how many tasks are waiting for them.&lt;/p&gt;

&lt;p&gt;Now you can spend. Ten minutes and one task waiting is a very different problem from ten minutes and three, and only that second pass tells you which one you are in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Most of the delay is the interface
&lt;/h2&gt;

&lt;p&gt;What eats the clock is usually the interface. The dragging and the dropdowns. Working out where this particular question even wants you to click.&lt;/p&gt;

&lt;p&gt;That cost is avoidable, and free. PBQs come in a handful of recognizable shapes, and each one has a rhythm you can pick up in an afternoon at your own desk with nothing riding on it, which is a far better place to meet a new interface than a testing center with a countdown running in the corner of the screen.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Drag one list onto another&lt;/li&gt;
&lt;li&gt;Fill in a configuration&lt;/li&gt;
&lt;li&gt;Sort items into buckets&lt;/li&gt;
&lt;li&gt;Click the right spot on a diagram&lt;/li&gt;
&lt;li&gt;Put a sequence of steps in order&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Never done one? &lt;a href="https://secplusmastery.com/r/ch-devto" rel="noopener noreferrer"&gt;This free firewall PBQ&lt;/a&gt; takes a few minutes and needs no account. It is the same kind of task. Do it badly once at home, where it costs nothing. Full disclosure: I built that one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The firewall task, since that is the one people dread
&lt;/h2&gt;

&lt;p&gt;The configuration PBQ has a reputation it mostly does not deserve, because the rules that decide it are short.&lt;/p&gt;

&lt;p&gt;A firewall rule list is read from the top down and the first matching rule wins. Nothing below a match gets consulted. That is it. Two things follow from that one sentence, and between them they account for most of what a configuration task is actually able to test you on.&lt;/p&gt;

&lt;p&gt;A specific rule has to sit above the general rule that would otherwise swallow it. Permit one host to a port, then deny the whole subnet to that port, and the host still gets through. Reverse the two lines and it does not.&lt;/p&gt;

&lt;p&gt;Anything you did not explicitly permit is denied at the bottom. So what are these tasks really checking? The order of the permits sitting above that line.&lt;/p&gt;

&lt;p&gt;After that it is ports, and the set that turns up is small.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;443 for HTTPS&lt;/li&gt;
&lt;li&gt;22 for SSH&lt;/li&gt;
&lt;li&gt;3389 for RDP&lt;/li&gt;
&lt;li&gt;53 for DNS&lt;/li&gt;
&lt;li&gt;445 for SMB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CompTIA's published exam objectives name the protocols that matter under each domain, which is a better list to work from than a printout of every port ever assigned.&lt;/p&gt;

&lt;h2&gt;
  
  
  The last ten minutes
&lt;/h2&gt;

&lt;p&gt;Decide now what you will do with the last ten minutes. Ten minutes is enough to finish one task properly, or to half-finish three.&lt;/p&gt;

&lt;p&gt;Take the flagged PBQ you understood best and complete it. Then the next one. An item you left untouched because you were busy being thorough somewhere else is the most expensive thing on the exam, and it is the one part of the score that is entirely under your control.&lt;/p&gt;

&lt;p&gt;None of this replaces knowing the material. It is what stops you from being unable to show the material you already know, which is a much more annoying way to fail.&lt;/p&gt;

</description>
      <category>security</category>
      <category>cybersecurity</category>
      <category>career</category>
      <category>learning</category>
    </item>
    <item>
      <title>The Security+ Acronyms That Get Confused With Each Other</title>
      <dc:creator>TiltedLunar123</dc:creator>
      <pubDate>Thu, 10 Sep 2026 09:48:36 +0000</pubDate>
      <link>https://dev.to/tiltedlunar123/the-security-acronyms-that-get-confused-with-each-other-58ib</link>
      <guid>https://dev.to/tiltedlunar123/the-security-acronyms-that-get-confused-with-each-other-58ib</guid>
      <description>&lt;p&gt;The acronym appendix at the back of the SY0-701 objectives lists more than three hundred entries. People often suggest memorizing it, and every study group has someone printing it out. That advice misreads what the exam does with acronyms.&lt;/p&gt;

&lt;p&gt;SY0-701 rarely tests what an acronym means. Instead, it gives you a short scenario and four options, two of which are acronyms from the same area, and asks which one fits the scenario. Knowing that RPO expands to recovery point objective doesn't help much on that question. Understanding that &lt;strong&gt;RPO measures lost data while RTO measures lost time&lt;/strong&gt; gets you the answer in about four seconds.&lt;/p&gt;

&lt;p&gt;So, the useful way to study is by cluster rather than alphabetically. A cluster is a small group of acronyms sitting close enough together that a question writer can build a believable wrong answer out of one of them. How many are there? Maybe fifteen in the whole exam. Here are five of the most common ones, with the objective each sits under, so you can read the source material.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four recovery numbers
&lt;/h2&gt;

&lt;p&gt;Objective 5.2 covers all four under business impact analysis; RPO and RTO also appear in 3.4 with resilience and recovery. Two of them are business targets. The other two are post-event observations. That split is the whole cluster. Learn it and the four stop blurring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RPO looks backward.&lt;/strong&gt; How much data can you afford to lose? That makes it about backup frequency. &lt;strong&gt;RTO looks forward.&lt;/strong&gt; How long can you afford to be down? Which is a claim about recovery capability, not about backups. MTTR is the average repair time. MTBF is the time between failures.&lt;/p&gt;

&lt;p&gt;The stem usually gives it away. Read it twice. A sentence about hourly snapshots points at RPO, and one about a four-hour outage window points at RTO. If the sentence describes something that already happened rather than a business decision, you are in MTTR and MTBF territory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Federation, and why one of these is not authentication
&lt;/h2&gt;

&lt;p&gt;Objective 4.6 puts SAML, OAuth and OIDC on one line with Kerberos, LDAP and RADIUS. The trap is that people learn the first three as single sign-on protocols and then mix them up under pressure. All three are very commonly confused.&lt;/p&gt;

&lt;p&gt;SAML and OIDC both carry identity. They answer who this person is. OAuth on its own does something else. It answers what an application is allowed to reach (authorization), and it was built to let an app touch your data without handing over your password. OIDC sits on top of OAuth 2.0 and adds the identity layer that OAuth left out.&lt;/p&gt;

&lt;p&gt;So, a stem describing a third-party app getting limited access to an account points at OAuth, even when the word login appears somewhere in the sentence. A browser redirected to a corporate identity provider, coming back with an assertion? SAML.&lt;/p&gt;

&lt;h2&gt;
  
  
  The agreement alphabet
&lt;/h2&gt;

&lt;p&gt;Objective 5.3 covers third-party risk, and this might be the least loved cluster; it feels like paperwork rather than security. It is also the one where every option in the question is a real document and only one of them fits the sentence you were given. Read the sentence, not the options.&lt;/p&gt;

&lt;p&gt;An &lt;strong&gt;SLA&lt;/strong&gt; carries numbers: uptime and response time, with a penalty for missing the number. An &lt;strong&gt;MOU&lt;/strong&gt; only records that two parties intend to work together, and it is generally not built to be enforced. An &lt;strong&gt;MSA&lt;/strong&gt; sets the terms of a long relationship, while the &lt;strong&gt;SOW&lt;/strong&gt; underneath it says what this particular job is and when it is due. An &lt;strong&gt;NDA&lt;/strong&gt; is about confidentiality and nothing else. A &lt;strong&gt;BPA&lt;/strong&gt; governs a partnership where both parties carry real risk if it goes wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Severity is not priority
&lt;/h2&gt;

&lt;p&gt;CVSS, EPSS and KEV all sit in 4.3. CVSS rates how bad a vulnerability would be if exploited. EPSS estimates how likely exploitation is. KEV is a catalog of vulnerabilities already being exploited in the wild.&lt;/p&gt;

&lt;p&gt;Any question that gives you a patch queue and asks what to fix first checks whether you spotted that a severity score alone doesn't set the order. A high CVSS on something nobody is exploiting can wait. A medium that appears on the KEV list goes first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four ways to test code
&lt;/h2&gt;

&lt;p&gt;Also in 4.3. This is the cleanest cluster of the five. SAST reads source without running it. DAST attacks the running application and sees nothing of the source. IAST instruments the running application to see both. SCA ignores your code entirely and looks at what it depends on.&lt;/p&gt;

&lt;p&gt;The discriminator is almost always in the first clause of the stem. Third-party library with a known vulnerability? That is SCA. No source available means DAST, and a scan that runs before the build has been deployed anywhere is SAST.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to actually drill this
&lt;/h2&gt;

&lt;p&gt;Reviewing a cluster by reading it is the trap. That is the whole warning. A comparison table feels productive, and what it trains is recognition, an easier skill than the one being tested.&lt;/p&gt;

&lt;p&gt;The version that works is slower and more annoying. For each pair in a cluster, write one sentence that only one of the two can answer. Then cover the labels and read your own sentences back. If you wrote a sentence that both acronyms could satisfy, you found the exact gap the question writer is going to aim at, and you found it at your desk instead of in the testing center.&lt;/p&gt;

&lt;p&gt;A last word on that appendix. Treat it as a scope boundary; some entries appear only once, in one sub-bullet of one objective. The clusters are where the questions live, and the fastest way to find the rest is to read CompTIA's published exam objectives and look for lines where three or four related acronyms are grouped together. If you would rather practice this the way the exam asks it, there is a &lt;a href="https://secplusmastery.com/r/ch-devto" rel="noopener noreferrer"&gt;free performance-based question here&lt;/a&gt; that needs no account. Full disclosure: I built that one.&lt;/p&gt;

</description>
      <category>security</category>
      <category>cybersecurity</category>
      <category>career</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
