Laravel News Links
Liquid Fluoride Can Stop Cavities Without Drilling, Major Trial Finds
https://gizmodo.com/app/uploads/2026/08/teeth-with-cavities-1280×853.jpg
Dentists might soon have an easier option on hand to treat the dreaded cavity.
Researchers at the University of Michigan conducted a large-scale Phase III trial testing out a liquid form of fluoride on children’s cavities. Compared to placebo, the treatment was significantly better at preventing further tooth decay and avoiding the need for more invasive options like fillings or surgery.
The findings show that liquid fluoride can be an effective “noninvasive treatment for young high-risk children and [support] its consideration for FDA drug approval,” the researchers wrote in their paper, published in July in JAMA Pediatrics.
Currently off-label
Cavities (also called caries) are holes caused by bacteria that erode the protective surface of our teeth. Though people these days might be taking better care of their teeth than in the past, more than 40% of children in the U.S. still develop some level of teeth decay. Cavities are usually treated using fillings, but in more severe cases where the decay penetrates deeper and causes an infection, people will need a more invasive root canal or even a complete removal of the affected teeth.
Fluoride has long been known to help prevent cavities, which is why governments in many parts in the world add small amounts of it to their drinking water supply. It also seems to be effective at preventing, or arresting, the further progression of existing cavities. Many countries have approved certain fluoride-based treatments, such as silver diamine fluoride (SDF), to prevent worsening cavities. Notably, however, this list doesn’t include the U.S.
In 2014, the Food and Drug Administration did approve SDF for treating tooth hypersensitivity, which allowed dentists to use it as an off-label treatment for cavities. Two years later, in 2016, the FDA designated SDF as a “breakthrough” therapy, a label intended to speed up the review process for treatments that can meet an urgent need. Without high-quality evidence of its safety and effectiveness, though, the FDA is unlikely to ever approve SDF for this use.
This current trial, funded in part by the U.S. National Institutes of Health, is meant to help provide that evidence.
Solid evidence
The trial involved 830 children under the age of six with severe early childhood cavities. Half of the children were randomized to get a placebo, and the remaining were given a 38% formulation of SDF, applied directly to the affected teeth via a small sponge-filled tip. People received treatment at the start of the study and a second dose six months later. The children were evaluated for cavities over the next eight months.
Ultimately, 70% of children completed the study in full. By the eight-month mark, about 50% of the children treated with SDF showed no progression of their cavities, compared to 17% of children given a placebo.
SDF is known to cause a permanent darkening of the teeth (an effect of the silver). That said, reported adverse events were similar between the two groups. This cosmetic side effect is also likely to be less worrying for children who will eventually lose their baby teeth.
The team’s findings should help strengthen the case for the FDA’s approval of SDF, the researchers say.
“If we want more children and families to benefit from this treatment, we need rigorous evidence showing both that it works and that it’s safe,” said lead author Margherita Fontana, professor of dentistry at the University of Michigan School of Dentistry, in a statement from the university. “From a public health perspective, if we want broader implementation across the United States, including in medical settings, we need carefully collected data in U.S. populations, and we now have that.”
Though the primary use of SDF is likely to be for very young children, it might also be a helpful stopgap for adults who are unable to get or afford other treatments.
“For almost anyone, this can arrest the decay and stop the infection and the pain it causes,” Fontana said. “This could benefit many people.”
Gizmodo
Don’t Be That Guy: The Taxonomy of Lousy Male Friends
https://content.artofmanliness.com/uploads/2010/08/dunce-1.jpg

Editor’s note: This is a guest post from Steve Kamb.
We all know “That Guy,” and we all have at least one in our group of friends.
He’s the one scalawag who is generally pleasant to hang out with except for one glaringly painful characteristic. Although there are different species of That Guy, some far more deplorable than others, they all have one thing in common: they often piss their friends off without knowing it.
Today, you’ll learn about the various forms of That Guy, how to react should you find one in your midst, and how to evolve in case you are That Guy.
The Poor Sport (Crybabial Sporticus)

Characteristics: The Poor Sport is dreadful to play against in any sporting event or competition. He complains incessantly about bad foul calls in pickup basketball, a weekend golf match among friends causes a temporary loss of arithmetic skills, and he will never accept blame for a loss, no matter the occasion. The cards are unlucky in a bad poker loss, the controller is faulty in a video game defeat, and the racquet is the reason for a poor tennis performance. In his own mind, he can do no wrong.
How to deal with That Guy: To start, don’t let him get away with cheating. If you know he counted incorrectly in golf, kindly remind him about the six shots it took him to get out of the bunker. If you defeat him in any competition, brace yourself for a torrent of expletives and excuses, but don’t fan the flames. Although you might want to defend your stellar performance or yell at him for being a crybaby, everybody around you already knows the truth: your friend is a sore loser.
How to evolve if you are That Guy: Go ask a few people about their worst “bad beat” in poker. Exhausted of the complaining yet? This is what you sound like to your friends ALL THE TIME. First of all, stop cheating in golf — your life won’t change if you post a 79 or a 119, and your friends ARE keeping track of your score whether they admit it or not. Show a little sportsmanship and integrity. If you lose to your friends at Halo, pickup basketball, tennis, scrabble, bocce, cards, whatever — take it like a man, and accept defeat. Everybody loses at some point, and nobody wants to hear excuses. Get over it.
The Mooch (Dudicus Moochalum)

Characteristics: The Mooch never has any money, brings any food, or provides any shelter; he instead leeches off of those around him. Think Cosmo Kramer in real life. The cause is most likely an allergic reaction to ambition. The last beer in the case, the final ice cream bar in the freezer, and any leftovers in the fridge tend to disappear whenever he’s around. A Mooch’s wallet gets “accidentally” left at home quite frequently, and he is always a pain to track down for repayment of money borrowed.
How to deal with That Guy: The Mooch usually understands his unfortunate situation, but he’s often too lazy to do anything about it. If you’re feeling magnanimous, help him find a job; just be careful who you recommend him to because it’s your reputation that’s at stake. Now, until he finds employment, do what you can to keep him in line: keep track of how much money you’ve loaned him and consider charging 10% interest each week until the debt is repaid. Don’t be afraid to call him out when he eats the last Oreo either, because that’s just not cool.
How to evolve if you are That Guy: Stop being lazy and get a job (and if you have a good job, quit mooching; the only thing worse than a poor mooch is a well-off cheapskate). Secondly, NEVER take the last one of something that isn’t yours unless you plan on replacing it. Try chipping in every once and awhile, too — show up unexpectedly with a case of beer or volunteer to buy the first round of drinks. I don’t care if you have to rob somebody first, always repay your financial debts to friends within 24 hours, no exceptions. Now, if you already have a job, and you’re still poor, find another way to contribute: are you a good cook? Do you have connections at a restaurant or movie theater? Do what you can to make up for your lack of funding — your friends will appreciate it.
Mr. Unreliable (Amigus Bail’Outicum)
I told the flake to pick me up at 7. Damnit!
Characteristics: It’s never known if Mr. Unreliable is actually going to show up, no matter how many promises have been made. He often signs up for an engagement before backing out at the last minute. “On time” to him usually means at least an hour late. Getting Mr. Unreliable to commit to something that requires an upfront financial deposit is like pulling teeth. Rather than saying no to an obligation, he’ll give a noncommittal answer that allows him to bail out at the last minute with a lame excuse via text message, thus avoiding confrontation.
How to deal with That Guy: Always expect Mr. Unreliable to not show up, and then be pleasantly surprised if he does! If you’re planning a trip with him, make sure to get some sort of financial commitment before putting up your own money — when he tries to back out this time, allow him to only do so if he can find a replacement. At that point, it’s no longer your responsibility. Not surprisingly, you should rely upon Mr. Unreliable as little as possible.
How to evolve if you are That Guy: Your friends don’t think you’ll show up to anything anymore, and eventually they’ll just stop calling. Change that perception by actually showing up to stuff consistently! What a novel idea, I know. Now, if you get invited to an event you don’t want to attend, be up front with your friends and tell them not to expect you. Lastly, start showing up on time. Arriving late consistently tells your friends that your time is more valuable than theirs. Show up at the right place at the right time.
The One-Upper (Betterum Than’Youicus)

Characteristics: Reeking of superiority and elitism, the One-Upper is a constant name-dropper of famous people and locations. During story time, the One-Upper must always ensure that he appears the strongest, best, had the toughest life growing up, drank the most beers, and/or met the coolest people. This is mostly due to a strong sense of insecurity, causing a need for constant affirmation and attention.
How to deal with That Guy: As tempting as it is to get into a pissing match with the One-Upper, the best course of action is to give him his small moment of glory, because he clearly needs it. As explained in Ben Franklin’s Virtuous Life Series, “People may talk about that guy’s exciting story the next day, but they’ll remember how much of a gentleman you are years later.” Be thankful you don’t base your self-worth on how much attention you can get. Go about your business, do great work, and the results will speak for themselves.
How to evolve if you’re That Guy: Nobody really cares how many famous people you know, how much you can bench, or how much better you are at something than everybody else. Start by letting other people have the spotlight every once and a while; continually one-upping your friends is a surefire way to piss everybody off. Instead, pick your battles and share stories when appropriate — not to brag, not to show off, but just to share a great story.
The Fibber (Fullofum Crapolakis)

Characteristics: The Fibber can stretch the truth like a penny-pincher can stretch a dollar. Known to tell dull stories that suddenly become way more exciting (and ultimately completely unbelievable), The Fibber is also known for creating inane excuses when trying to weasel out of any scenario. The Fibber is closely related to Mr. Unreliable and the Poor Sport for similar “lack of truth” qualities.
How to deal with That Guy: Take everything The Fibber says with a grain of salt and don’t bother wasting your time trying to trap him in a lie — he’ll simply get defensive and start weaving another web of lies. Just be thankful that you’re not a liar and take solace in the fact that people can rely on your word. Now, if the lies start to become disruptive to the group, pull the person aside and have a serious conversation about the lies in private rather than calling him out in public.
How to evolve if you’re That Guy: You know you’re lying, your friends know you’re lying, and that hole you’re digging for yourself is only getting deeper. Instead of creating new tall tales and more complex lies to cover for the old ones, just freaking tell the truth and wait until you actually have a good story to tell! Your conscience will thank you and so will your buddies.
Other Lesser Known Species of That Guy
Other than the five prominent species of That Guy listed above, there are actually quite a few others who might be lesser known but are no less abysmal:
The Flip Flopper (Fencium Sitterus). Having no opinion of his own, the Flip Flopper will alter his beliefs depending on who he’s talking to and who he’s trying to impress. He is generally classified as an invertebrate for lacking a backbone.
The Loud Mouth (Pieholus Gigantum). This chump cannot keep his mouth shut, whether it’s keeping a secret, talking during a movie, or putting down another friend when he’s not around. The Loud Mouth generally should not be trusted with any important information unless it needs to be shouted from the rooftops.
The “Takes It Too Far” Guy (Over The’Lineicus). This poor soul lives life to the extreme a little too often. He’s generally the one that always gets WAY too drunk at parties, takes jokes too far after they’ve become unfunny, and gets offended over things that nobody else would ever take personally. Tact is pretty much non-existent.
“The Garbage Man” (In’lovum with Jesses’girlikus). The Garbage Man has no problem attempting to date his friend’s ex-girlfriend, because he lacks the skills to move outside of the social circle and meet new people. The most despicable variation of this species will attempt to date his friend’s current girlfriend, which generally results in an ass-kicking.
Steve Kamb is the founder of Nerd Fitness and the author of How to Try Again. You can find him on Substack.
Be sure to listen to our podcast with Steve about his latest book:
__________________________________________________________________________
With our archives now 4,000+ articles deep, we’ve decided to republish a classic piece each Sunday to help our newer readers discover some of the best, evergreen gems from the past. This article was originally published in August 2010.
This article was originally published on The Art of Manliness.
The Art of Manliness
McDonald’s Backpack
https://www.toxel.com/wp-content/uploads/2026/07/mcfriesbackpack01.jpg
August 1st, 2026 | Tech |

McDonald’s fast food restaurants in China sell limited edition backpacks with side pockets for large Fries and ketchup.
Designed with iconic McDonald’s colors and Golden Arches, the backpack features oversized side pockets that hold large servings of fries.
Shoulder strap includes a built-in storage loop for holding ketchup packets.

Instead of telling people to love fries, McDonald’s created a backpack that lets fans literally carry and display their love for fries wherever they go.

Big Fries Backpack celebrates fast food and does not take itself too seriously.

Most backpacks are designed to carry books, laptops, or clothing. This one proudly makes room for McDonald’s fries.

Included shoulder-strap fry holder allows people to snack hands-free while walking, almost like a wearable dining system.

Also check out: McDonald’s Fries Crosswalk
Toxel.com
MySQL Performance Tuning: 8 Proven Techniques for Faster Queries
https://webyog.com/wp-content/uploads/2026/07/MySQL-performance-tuning.png
Slow MySQL queries are one of the most common database problems — and one of the most solvable. Most performance issues don’t require new hardware or a rewrite. They require knowing where to look and what to fix first.
These eight techniques cover the most impactful improvements, ordered by how quickly they tend to deliver results.
Start With a Map, Not a Guess
The biggest mistake in performance tuning is skipping straight to server configuration changes without knowing where the actual bottleneck is. A misconfigured server running well-optimized queries will outperform a perfectly tuned server running terrible ones.

Skipping steps 1–3 and jumping to step 4 is how teams waste weeks tuning a server without improving anything meaningful.

1. Find Slow Queries First
Enable the MySQL slow query log to capture every query that takes longer than a threshold you define.
SET GLOBAL slow_query_log = ‘ON’;
SET GLOBAL long_query_time = 1;
This creates a log of every query that takes more than one second. Review it with mysqldumpslow (bundled with MySQL) to find your worst offenders.
Fix the top 10 slowest queries before doing anything else. In most databases, a handful of bad queries account for the majority of performance problems.
2. Read EXPLAIN Before Writing a Single Index
Run EXPLAIN before any slow SELECT query to see how MySQL plans to execute it. The most
important column is type .

If you see ALL on a table with meaningful data, there is almost certainly a missing index.
3. Add the Right Indexes
Adding an index on a column you filter by frequently is the single highest-return change you can make in most databases. It can turn a 10-second query into a millisecond one.
At the same time, every index adds overhead to writes. Review indexes regularly and remove ones that are no longer used:
SELECT * FROM sys.schema_unused_indexes;
A table with 15 indexes will have noticeably slower writes. Keep only what you use.
4. Fix the Queries Themselves
Before adjusting any server setting, look at the query logic. A few common patterns that cause avoidable slowness:

These changes require no schema modifications and often improve performance significantly on their own.
5. Size the InnoDB Buffer Pool Correctly
The InnoDB buffer pool is MySQL’s memory cache for data and indexes. If it is too small, MySQL reads from disk on every query — and disk is orders of magnitude slower than memory.
On a server dedicated to MySQL, allocate 70–80% of total RAM to the buffer pool. Check whether your current setting is adequate by looking at the buffer pool hit rate in SHOW ENGINE INNODB STATUS . If the hit rate is below 99%, the buffer pool is likely undersized.
6. Use Connection Pooling
Every time an application opens a fresh MySQL connection, there is overhead — authentication, session setup, memory allocation. For applications handling many concurrent requests, this adds up quickly.
Connection pooling maintains a set of open connections that are reused across requests. The application borrows a connection, uses it, and returns it. MySQL sees a small, stable number of connections regardless of application traffic.
Most application frameworks include built-in connection pooling. Enable it if you have not.
7. Monitor Query Execution in Real Time
Performance problems in production often appear under load — not during development or testing. You need visibility into what your database is doing right now, not just after the fact.
MONyog provides live dashboards showing active sessions, running queries, lock waits, and thread state. Its built-in advisors surface configuration issues and query patterns that commonly cause problems before they become incidents.

8. Tune Configuration Variables
Once queries and indexes are optimized, server configuration adjustments can provide additional gains. Key settings to review:

Always benchmark before and after configuration changes. A setting that helps one workload can hurt another.
Quick-Start Checklist

Want real-time visibility into your MySQL performance? Try MONyog free — monitor query performance, sessions, and server health in minutes. No agents, no overhead
Frequently Asked Questions
Enable the slow query log with long_query_time = 1 and let it run for a few hours. Find your ten slowest queries, run EXPLAIN on each one, and look for type: ALL in the output. In most environments, fixing a handful of queries with missing indexes will resolve the majority of the problem.
On a dedicated MySQL server, 70–80% of total available RAM. On a shared server, start at 50% and monitor. The goal is to keep your working dataset — the data and indexes accessed most frequently — in memory rather than on disk.
No. Every index adds overhead to writes. Too many indexes will slow INSERT, UPDATE, and DELETE operations noticeably. Add indexes based on actual slow query patterns, and remove unused ones regularly using sys.schema_unused_indexes .
Run SHOW ENGINE INNODB STATUS and look for the buffer pool hit rate. Below 99% typically indicates the buffer pool is undersized relative to your working dataset. Also monitor Innodb_buffer_pool_reads versus Innodb_buffer_pool_read_requests .
Usually because production has significantly more data, making table scans that were tolerable in testing very slow at scale. Or production has concurrent load creating lock contention. Enable the slow query log in production and run EXPLAIN — the execution plan can differ from what you saw in testing.
MONyog is Webyog’s MySQL monitoring tool. It provides real-time dashboards showing active queries, session activity, lock waits, and server health metrics. Its 600+ built-in advisors surface configuration issues and performance patterns automatically, alerting your team before users notice a problem.
Most improvements — adding indexes, optimizing queries — can be done on a live production database without downtime. MySQL 5.6+ supports online index creation for InnoDB tables. Configuration changes typically require a MySQL restart, though some can be applied dynamically. Plan configuration changes during low-traffic windows.
Review the slow query log weekly in active development periods and monthly in stable production environments. Set up real-time monitoring so you are alerted to performance regressions immediately rather than discovering them during manual reviews.
Planet for the MySQL Community
A Super Satisfying Marble Run
https://theawesomer.com/photos/2026/07/massive_marble_run_t.jpg
Jelle’s Marble Runs built this impressive display using 19,000 individual marbles along with parts from Xyloba and Gravitrax. The run starts slowly, but builds to a big finish with fields filled with thousands of hand-placed marbles laid out in pixel-art patterns. It took him five days to set it all up, and just a few minutes for all of the marbles to cascade through.
The Awesomer
Laravel Schema Designer
https://opengraph.githubassets.com/dde2f2f98f219065e9b78f42e0904f189e3bee6b38c303db761001279769d73a/hussein4alaa/schema-designer
A visual database schema designer for Laravel, driven entirely by migration files — no database introspection required.
The package replays every migration’s up() method against a capturing schema builder swapped in behind the Schema facade. Blueprints are recorded instead of executed and folded into an in-memory model of your final schema — rendered as an interactive ERD. Every change you make in the UI (create / edit / drop a table) generates a real migration file, shown to you for review before it is written.
composer require g4t/schema-designer
The service provider is auto-discovered. Open the designer at:
http://your-app.test/schema-designer
- ERD canvas — tables as draggable cards, foreign keys as curved colored edges, pan/zoom, auto-layout, fit-to-view, and a live filter. Card positions persist across sessions.
- Create tables — column editor with all common Blueprint types, length/precision/enum values, nullable/unsigned/default/
useCurrentmodifiers, composite indexes, and foreign keys withonDelete/onUpdate. Quick-add buttons forid,uuid,timestamps, and soft deletes. - Edit tables — the designer diffs your changes against the parsed schema and generates an update migration:
renameColumn,->change(),dropColumn, index and FK adds/drops — ordered correctly (FK/index drops before column drops) with a best-effortdown(). - Drop tables — generates a
dropIfExistsmigration whosedown()fully recreates the table from the current parsed definition. - Migration awareness — tables whose source migrations haven’t run yet get a pending badge, and you can run
php artisan migrateright from the UI. - Safe parsing — raw
DB::statement()/ query-builder calls inside migrations run in the connection’s pretend mode, so parsing never touches your database. Files that fail to parse are surfaced as warnings, never fatal.
| Laravel | 8.x, 9.x, 10.x, 11.x, 12.x, 13.x |
| PHP | ^8.0 |
| Databases | Driver-agnostic — parsing happens at the Blueprint level, so MySQL, PostgreSQL, SQLite, and SQL Server projects all work the same. |
php artisan vendor:publish --tag=schema-designer-config
| Key | Default | Purpose |
|---|---|---|
enabled |
null |
null → available everywhere except production. Override with SCHEMA_DESIGNER_ENABLED=true/false. |
path |
schema-designer |
Route prefix. |
middleware |
['web'] |
Add auth etc. to protect the UI. |
migrations_path |
database_path('migrations') |
Where migrations are read from and written to. |
layout_file |
storage/app/schema-designer/layout.json |
Canvas positions. |
- Migration files are loaded in order (both anonymous-class and classic named-class styles).
- The
Schemafacade root is swapped for aCapturingSchemaBuilder; eachSchema::create/table/drop/renamecall produces a Blueprint that is recorded, not executed. - Introspection calls inside migrations (
Schema::hasTable(),hasColumn(), …) are answered from the aggregated state, so conditional migrations behave exactly as they would against a fully migrated database. SchemaAggregatorfolds columns,->change()s, renames, drops, indexes, and foreign keys into the final schema model.
- Raw SQL (
DB::statement('ALTER TABLE …')) can’t be interpreted structurally — the statement is safely ignored and the file is listed under parse warnings when it affects schema. Schema::connection('other')->…calls bypass the capture and are not represented.- Generated
down()methods for edits are best-effort reconstructions.
The UI writes files into your migrations directory and can run php artisan migrate. It is disabled in production by default — if you enable it there, put real auth middleware in front of it.
MIT
Laravel News Links
A first look at MySQL 26.7 Early Access
https://ronaldbradford.com/images/blog/mysql-26-7-early-access.png
MySQL has dropped its newest release
, categorized as “Early Access” and available at https://labs.mysql.com/
.
While this post is not going to go into depth, I wanted to at least validate the management changes you verify between normal MySQL upgrades.
The new release version is `26.7`, the first version with a date-based release number convention. While this is numerically increased, the first problem I found was a character ordering problem not found in prior scripts. This is because 2 is before 9, historically for any version, the versions listed alphabetically would always list older before newer. MySQL isn’t the only product that uses this naming convention, or the first to switch from one to the other, however some random MySQL script on some customer installation is going to have some small issue and there will be the unnecessary followup flamewar. See later as to why I mentioned this.
Docker Containers Setup
Preamble
TMP_DIR=${TMP_DIR:-/tmp}
TEST_CASE="first-look"
rm ${TMP_DIR}/${TEST_CASE}.*
Install MySQL 9.7
The following will install the current MySQL 9.7 docker version.
REGISTRY_NAME="container-registry.oracle.com/mysql/community-server:9.7"
CONTAINER_NAME="mysql097"
docker pull ${REGISTRY_NAME}
MYSQL_PASSWD="M#$(date | md5sum - | cut -c-20)"
OUTPUT=$(docker run -d --name ${CONTAINER_NAME} \
--platform linux/amd64 \
-e MYSQL_ROOT_PASSWORD=${MYSQL_PASSWD} \
${REGISTRY_NAME})
echo $?
echo $OUTPUT
# Not completely accurate
# docker logs ${CONTAINER_NAME} | grep "ready for connections"
sleep 5
docker exec -it ${CONTAINER_NAME} mysql -uroot -p${MYSQL_PASSWD} -sN -e "SELECT VERSION()" | grep -v "can be insecure"
docker stop ${CONTAINER_NAME} && docker rm ${CONTAINER_NAME}
Install MySQL 26.7 EA
In a separate terminal window run.
# See https://labs.mysql.com/ to obtain valid and current direct download links
wget https://downloads.mysql.com/snapshots/pb/mysql-26.7.0-labs-release/mysql-community-server-26.7.0-labs-docker-el9-x86_64.tar.gz
docker load -i mysql-community-server-26.7.0-labs-docker-el9-x86_64.tar.gz
REGISTRY_NAME="localhost/mysql/community-server:26.7.0"
CONTAINER_NAME="mysql267"
...
New Variables
The 26.7 Release Notes
are not accompanied by a current reference Manual (e.g. 9.7
) so this default checking gives a first approximate look.
What it doesn’t find is for example the setup for a replica, or different distros, or following the installation of any components in which there is noted new work.
# Run for both containers
$ docker exec -it ${CONTAINER_NAME} mysql -uroot -p${MYSQL_PASSWD} -s -e "SELECT VERSION(); SELECT VARIABLE_NAME FROM performance_schema.global_variables ORDER BY 1" > ${TMP_DIR}/${TEST_CASE}.variables.${CONTAINER_NAME}.txt
$ diff -y --suppress-common-lines ${TMP_DIR}/${TEST_CASE}.variables.*.txt
9.7.1 | 26.7.0-er
> admin_force_pqc
> admin_tls_kex
> admin_use_pqc_sign
> force_pqc
> innodb_autoinc_preallocate
> mysqlx_force_pqc
> mysqlx_tls_kex
> mysqlx_use_pqc_sign
> replication_force_pqc
> replication_tls_kex
> replication_use_pqc_sign
> tls_kex
> use_pqc_sign
One of my primary goals in further evaluation is the Post-quantum cryptography support with OpenSSL 3.5. You can find some intro information in my post Q Day Is Coming: A Plain-English Guide to Post-Quantum Cryptography
. Almost all these variables align with this functionality. The kex variables is the standard abbreviation for key exchange. The pqc variables are related to the negotiation of a PQC (or Hybrid) group. As you can see in the next section, all pqc related variables are OFF.
Based on this information, without looking into source, the force_pqc, tls_kex and use_pqc_sign would be the required three variables to enable post quantum key exchange.
Variable Values
$ docker exec -it ${CONTAINER_NAME} mysql -uroot -p${MYSQL_PASSWD} -s -e "SELECT VERSION(); SELECT VARIABLE_NAME, VARIABLE_VALUE FROM performance_schema.global_variables ORDER BY 1" > ${TMP_DIR}/${TEST_CASE}.var-value.${CONTAINER_NAME}.txt
$ diff -y --suppress-common-lines ${TMP_DIR}/${TEST_CASE}.var-value.*.txt | grep ">" | cut -d'>' -f2-
admin_force_pqc OFF
admin_tls_kex
admin_use_pqc_sign OFF
force_pqc OFF
innodb_autoinc_preallocate 50
mysqlx_force_pqc OFF
mysqlx_tls_kex
mysqlx_use_pqc_sign OFF
replication_force_pqc OFF
replication_tls_kex
replication_use_pqc_sign OFF
tls_kex
There would appear to be no changes to default values for existing variables, however this is just the sandbox docker installation.
Alphabetical and Chronological
I mentioned earlier about chronological and alphabetical issues. This is what I mean. My line of code was to diff mysql97 and mysql267 where previously these would wash out alphabetically, but this does not happen now (i.e. historically it would be mysql8, mysql84, mysql97). mysql267 preceeds mysql97, and hence why the container for simplicity of commands is called mysql097.
$ diff -y --suppress-common-lines ${TMP_DIR}/${TEST_CASE}.variables.*.txt
26.7.0-er | 9.7.1
admin_force_pqc <
admin_tls_kex <
admin_use_pqc_sign <
force_pqc <
innodb_autoinc_preallocate <
mysqlx_force_pqc <
mysqlx_tls_kex <
mysqlx_use_pqc_sign <
replication_force_pqc <
replication_tls_kex <
replication_use_pqc_sign <
tls_kex <
use_pqc_sign <
New Status Variables
$ docker exec -it ${CONTAINER_NAME} mysql -uroot -p${MYSQL_PASSWD} -s -e "SELECT VERSION(); SELECT VARIABLE_NAME FROM performance_schema.global_status ORDER BY 1" > ${TMP_DIR}/${TEST_CASE}.status.${CONTAINER_NAME}.txt
$ diff -y --suppress-common-lines ${TMP_DIR}/${TEST_CASE}.status.*.txt
9.7.1 | 26.7.0-er
> Mysqlx_force_pqc
> Mysqlx_tls_kex
> Mysqlx_use_pqc_sign
There would appear to be three new status variables related only to the mysqlx access of the post quantum support. Nothing for admin or replication, however again this is a primary and not a replica.
Status Values
Looking at values on two different systems is more complicated for many factors, however a quick scan reveals no obvious changes in volumes or measure.
$ diff -y --suppress-common-lines ${TMP_DIR}/${TEST_CASE}.status-value.*.txt
9.7.1 | 26.7.0-er
Bytes_received 2115 | Bytes_received 3343
Bytes_sent 52456 | Bytes_sent 90408
Caching_sha2_password_rsa_public_key -----BEGIN PUBLIC KEY | Caching_sha2_password_rsa_public_key -----BEGIN PUBLIC KEY
Connections 14 | Connections 17
Error_log_buffered_bytes 1224 | Error_log_buffered_bytes 1752
Error_log_buffered_events 10 | Error_log_buffered_events 12
Error_log_latest_write 1784693965980559 | Error_log_latest_write 1784693440620348
Handler_commit 592 | Handler_commit 596
Handler_external_lock 6477 | Handler_external_lock 6489
Handler_read_key 1752 | Handler_read_key 1756
Handler_read_next 4160 | Handler_read_next 4166
Handler_read_rnd_next 1768 | Handler_read_rnd_next 3087
Innodb_buffer_pool_bytes_data 19775488 | Innodb_buffer_pool_bytes_data 19906560
Innodb_buffer_pool_load_status Buffer pool(s) load completed | Innodb_buffer_pool_load_status Buffer pool(s) load completed
Innodb_buffer_pool_pages_data 1207 | Innodb_buffer_pool_pages_data 1215
Innodb_buffer_pool_pages_flushed 199 | Innodb_buffer_pool_pages_flushed 198
Innodb_buffer_pool_pages_free 6985 | Innodb_buffer_pool_pages_free 6977
Innodb_buffer_pool_read_requests 16159 | Innodb_buffer_pool_read_requests 16200
Innodb_buffer_pool_reads 1063 | Innodb_buffer_pool_reads 1070
Innodb_buffer_pool_write_requests 1971 | Innodb_buffer_pool_write_requests 1993
Innodb_data_fsyncs 75 | Innodb_data_fsyncs 69
Innodb_data_read 17485312 | Innodb_data_read 17691136
Innodb_data_reads 1089 | Innodb_data_reads 1098
Innodb_data_writes 258 | Innodb_data_writes 253
Innodb_data_written 3336704 | Innodb_data_written 3312640
Innodb_dblwr_pages_written 58 | Innodb_dblwr_pages_written 57
Innodb_dblwr_writes 4 | Innodb_dblwr_writes 3
Innodb_log_write_requests 841 | Innodb_log_write_requests 840
Innodb_log_writes 20 | Innodb_log_writes 18
Innodb_os_log_fsyncs 16 | Innodb_os_log_fsyncs 12
Innodb_os_log_written 53760 | Innodb_os_log_written 52736
Innodb_pages_created 145 | Innodb_pages_created 146
Innodb_pages_read 1062 | Innodb_pages_read 1069
Innodb_pages_written 199 | Innodb_pages_written 198
Innodb_redo_log_checkpoint_lsn 29775665 | Innodb_redo_log_checkpoint_lsn 30006513
Innodb_redo_log_current_lsn 29775665 | Innodb_redo_log_current_lsn 30006513
Innodb_redo_log_flushed_to_disk_lsn 29775665 | Innodb_redo_log_flushed_to_disk_lsn 30006513
Innodb_redo_log_uuid 1560291093 | Innodb_redo_log_uuid 2583501521
Innodb_system_rows_read 4888 | Innodb_system_rows_read 4894
Max_used_connections_time 2026-07-22 04:19:37 | Max_used_connections_time 2026-07-22 04:10:50
> Mysqlx_force_pqc OFF
Mysqlx_ssl_server_not_after Jul 19 04:19:20 2036 GMT | Mysqlx_ssl_server_not_after Jul 19 04:10:35 2036 GMT
Mysqlx_ssl_server_not_before Jul 22 04:19:20 2026 GMT | Mysqlx_ssl_server_not_before Jul 22 04:10:35 2026 GMT
> Mysqlx_tls_kex
> Mysqlx_use_pqc_sign OFF
Open_tables 74 | Open_tables 63
Opened_tables 155 | Opened_tables 144
option_tracker_usage:Traditional Optimizer 15 | option_tracker_usage:Traditional Optimizer 23
Performance_schema_session_connect_attrs_longest_seen 112 | Performance_schema_session_connect_attrs_longest_seen 116
Queries 28 | Queries 44
Questions 27 | Questions 43
Rsa_public_key -----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9 | Rsa_public_key -----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9
Select_scan 4 | Select_scan 6
Sort_rows 1604 | Sort_rows 2921
Sort_scan 3 | Sort_scan 5
Ssl_server_not_after Jul 19 04:19:20 2036 GMT | Ssl_server_not_after Jul 19 04:10:35 2036 GMT
Ssl_server_not_before Jul 22 04:19:20 2026 GMT | Ssl_server_not_before Jul 22 04:10:35 2026 GMT
Table_locks_immediate 4 | Table_locks_immediate 6
Table_open_cache_hits 3084 | Table_open_cache_hits 3101
Table_open_cache_misses 155 | Table_open_cache_misses 144
Tls_library_version OpenSSL 3.5.1 1 Jul 2025 | Tls_library_version OpenSSL 3.5.5 27 Jan 2026
Uptime 7128 | Uptime 7658
Uptime_since_flush_status 7128 | Uptime_since_flush_status 7658
This is not an exhaustive comparison, however there appears to be no new table objects. There are 3 new replication applier related columns which align with the release notes.
$ docker exec -it ${CONTAINER_NAME} mysql -uroot -p${MYSQL_PASSWD} -s -e "SELECT VERSION(); SELECT TABLE_SCHEMA, TABLE_NAME FROM information_schema.tables ORDER BY 1,2" > ${TMP_DIR}/${TEST_CASE}.tables.${CONTAINER_NAME}.txt
$ diff -y --suppress-common-lines ${TMP_DIR}/${TEST_CASE}.tables.txt
docker exec -it ${CONTAINER_NAME} mysql -uroot -p${MYSQL_PASSWD} -s -e "SELECT VERSION(); SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION FROM information_schema.columns ORDER BY 1,2,4" > ${TMP_DIR}/${TEST_CASE}.columns.${CONTAINER_NAME}.txt
$ diff -y -W 400 --suppress-common-lines ${TMP_DIR}/${TEST_CASE}.columns.*.txt
9.7.1 | 26.7.0-er
> mysql slave_relay_log_info Applier_version 16
> mysql slave_relay_log_info Applier_worker_count 17
> mysql slave_relay_log_info Applier_event_memory_limit 18
> performance_schema replication_applier_configuration APPLIER_VERSION 8
> performance_schema replication_applier_configuration APPLIER_WORKER_COUNT 9
> performance_schema replication_applier_configuration APPLIER_EVENT_MEMORY_LIMIT 10
Plugins
There are no additional plugins found by comparing via SHOW PLUGINS, however the release notes reference the previously enterprise feature of Thread Pool Plugin now in MySQL Community Server. There are no installed components by default in the docker mode for either version.
Thread Pool
The use of the Thread Pool plugin requires a MySQL configuration modification for the mysqld process, which requires a restart.
$ OUTPUT=$(docker run -d --name ${CONTAINER_NAME} \
--platform linux/amd64 \
-e MYSQL_ROOT_PASSWORD=${MYSQL_PASSWD} \
${REGISTRY_NAME} \
--plugin-load-add=thread_pool.so)
Thread Pool Logs
Following the installation instructions for the Thread Pool
for MySQL Enterprise you can see this plugin in 26.7.
$ docker logs ${CONTAINER_NAME}
...
2026-07-22T06:58:28.845787Z 0 [System] [MY-013852] [Server] Thread pool plugin started successfully with parameters: thread_pool_size = 1, thread_pool_algorithm = High Concurrency Algorithm, thread_pool_stall_limit = 6, thread_pool_prio_kickup_timer = 1000, thread_pool_max_unused_threads = 32, thread_pool_max_active_query_threads = 0, thread_pool_dedicated_listeners = 0, thread_pool_max_transactions_limit = 32, thread_pool_transaction_delay = 0, thread_pool_query_threads_per_group = 2, thread_pool_connection_report_interval = 120, thread_pool_longrun_trx_limit = 2000 ; 2000
Thread Pool Plugins
There are now 4 additional plugins in 26.7 with the thread pool plugin enabled.
mysql> SHOW PLUGINS;
+----------------------------------+----------+--------------------+----------------+-------------+
| Name | Status | Type | Library | License |
+----------------------------------+----------+--------------------+----------------+-------------+
...
| thread_pool | ACTIVE | DAEMON | thread_pool.so | PROPRIETARY |
| TP_THREAD_STATE | ACTIVE | INFORMATION SCHEMA | thread_pool.so | PROPRIETARY |
| TP_THREAD_GROUP_STATE | ACTIVE | INFORMATION SCHEMA | thread_pool.so | PROPRIETARY |
| TP_THREAD_GROUP_STATS | ACTIVE | INFORMATION SCHEMA | thread_pool.so | PROPRIETARY |
+----------------------------------+----------+--------------------+----------------+-------------+
50 rows in set (0.023 sec)
Thread Pool Tables
Note, the Enterprise 9.7 documentation lists only 3 matching tables, tp_connections appears to be new.
mysql> SELECT TABLE_NAME
-> FROM INFORMATION_SCHEMA.TABLES
-> WHERE TABLE_SCHEMA = 'performance_schema'
-> AND TABLE_NAME LIKE 'tp%';
+-----------------------+
| TABLE_NAME |
+-----------------------+
| tp_connections |
| tp_thread_group_state |
| tp_thread_group_stats |
| tp_thread_state |
+-----------------------+
4 rows in set (0.075 sec)
Conclusion
This is a quick validation I can install and validate a running MySQL 26.7 sandbox. I will be following up in future posts with additional findings.
Planet for the MySQL Community
How Attackers Enumerate Your Laravel App And What to Hide Before They Map Your Entire Backend
Laravel News Links
Wirechat 0.6 adds content browsing, chat tabs, and group join requests
https://corepine.dev/assets/wirechat/preview-light.webp
Release Notes
Wirechat 0.6x expands the panel API with more opt-in chat experiences: settings, message requests, group invite links, join-request moderation, content browsing, tabs, tray support, and configurable models for the new records.
From 0.5x To 0.6x
Upgrading to 0.6x should be a small migration for most applications. The main work is updating the package, publishing the new migrations, and enabling the panel features your application needs.
Minimal Breaking Changes
Clear and delete actions are now opt-in panel features:
clearChatAction()is disabled by default.deleteChatAction()is disabled by default.
Wirechat also uses ColorTone::Soft by default in 0.6x. This is a visual breaking change: outgoing messages and related primary surfaces now use a softer tinted treatment instead of the stronger primary-color surface used before.
To keep the older stronger look, set the panel color tone to ColorTone::Solid:
use Wirechat\Wirechat\Enums\ColorTone;
use Wirechat\Wirechat\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->colorTone(ColorTone::Solid);
}
If your application already shows these actions and you want to keep them visible, enable them in your panel provider:
use Wirechat\Wirechat\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->clearChatAction()
->deleteChatAction();
}
Create a working branch in your application before upgrading:
git checkout -b wirechat-upgrade
Update your Composer constraint:
"wirechat/wirechat": "^0.6"
Then update the package:
composer update wirechat/wirechat
Publish the new migrations and run them:
php artisan vendor:publish --tag=wirechat-migrations
php artisan migrate
Clear compiled views after upgrading:
php artisan optimize:clear
If you have published Wirechat views, compare your local copies with the new package views before replacing them:
What Is New
Settings Drawer
Wirechat 0.6x introduces an opt-in settings drawer for each panel. When enabled, users get a Settings entry in the chats header. The first built-in section is notification preferences.
use Wirechat\Wirechat\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->settings();
}
The settings system introduces the wirechat_settings table and a DTO-based API for reading user preferences:
use Wirechat\Wirechat\Facades\Wirechat;
$settings = Wirechat::settings($user);
if ($settings->notification_previews_enabled) {
// Show notification preview text.
}
Settings are disabled by default. You may also enable them conditionally:
public function panel(Panel $panel): Panel
{
return $panel
// ...
->settings(fn () => auth()->user()?->can('manage-chat-settings') ?? false);
}
Read more on the Settings page.
Message Requests
Message requests let a sender start a private conversation without immediately adding the recipient as a participant. The recipient can open the pending thread, review it, and then accept or dismiss the request.
use Wirechat\Wirechat\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->messageRequests();
}
When enabled, the default new-chat UI creates review-first conversations. The sender can continue writing while the recipient sees an incoming request flow.
You can also create a request programmatically:
$conversation = auth()->user()->sendMessageRequestTo($recipient);
Accepting a request adds the recipient to the conversation. Dismissing it keeps the recipient out of the participant list and removes the pending request from the active flow.
Read more on the Message Requests page.
Group Invitations
Group invitations add shareable invite links, public invite preview pages, and in-app join handling for group conversations. They are enabled per panel:
use Wirechat\Wirechat\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->groupInvitations();
}
When enabled, owners and admins can manage invite links from group tools. Invite links can join a user immediately or create a join request, depending on the group access settings.
For public invite pages, you can choose the layout used outside the authenticated chat shell:
public function panel(Panel $panel): Panel
{
return $panel
// ...
->groupInvitations()
->invitePageLayout('wirechat::layouts.app');
}
If your chat runs as a widget with full chat routes disabled, point public invite hand-offs back to the page that renders the widget:
public function panel(Panel $panel): Panel
{
return $panel
// ...
->groupInvitations()
->registerRoutes(false)
->mountUrl(fn () => route('wirechat.widget'));
}
Read more on the Groups page.
Join Requests
Join requests are the moderation layer behind approval-based group invites. A valid invite does not always mean immediate membership. If the group requires approval, Wirechat creates a JoinRequest record instead.
$request = $conversation->group->requestToJoin($user, $invite);
$conversation->group->acceptPendingJoinRequest(
$user,
reviewedBy: $admin,
markInviteUsed: true,
);
$conversation->group->dismissPendingJoinRequest(
$user,
reviewedBy: $admin,
);
This introduces a safer group-join lifecycle:
- duplicate pending requests are reused instead of recreated
- accepted requests store reviewer metadata
- dismissed requests remain auditable
- invite usage is only counted when the join is accepted
Read more on the Groups page.
Content Viewer
Content Viewer gives a conversation a dedicated place to browse shared media, documents, and links without leaving the chat interface.
use Wirechat\Wirechat\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->contentViewer();
}
When enabled, Wirechat adds a content entry to the conversation details panel. Users can browse media, docs, and links from the current conversation, then jump back to the original message context.
Read more on the Content Viewer page.
Conversation Tabs
Tabs let you split the chats list into focused views such as All, Unread, or Groups.
use Illuminate\Database\Eloquent\Builder;
use Wirechat\Wirechat\Enums\ConversationType;
use Wirechat\Wirechat\Panel;
use Wirechat\Wirechat\Support\Tabs\Tab;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->tabs(
Tab::make('all'),
Tab::make('groups')
->label('Groups')
->query(fn (Builder $query) => $query->where('type', ConversationType::GROUP->value))
->count(),
)
->defaultTab('all');
}
Tabs refine the existing conversations query for the current user. Use them to keep large chat lists easier to scan without building a separate chats UI.
Read more on the Tabs page.
The tray widget adds a compact floating chat entry point that can live in your authenticated layout. It keeps chat available without forcing users to leave the page they are using.
<html>
<head>
@wirechatStyles
</head>
<body>
...
@wirechatAssets()
@auth
<livewire:wirechat.tray panel="chats" />
@endauth
</body>
</html>
The tray uses the selected panel, so it follows that panel’s routes, middleware, actions, tabs, attachments, and other feature settings.
You can customize the tray launcher and opened panel:
<livewire:wirechat.tray
panel="chats"
:limit="8"
class="bottom-3 right-3 w-[28rem]"
launcherClass="rounded-full px-4 py-2 shadow-lg"
heading="Inbox"
/>
Read more on the Tray page.
Notification Preferences
Web push notifications now work with user-owned notification settings when the settings drawer is enabled. Realtime broadcasts still update unread counts and chat lists, but browser notifications respect these preferences:
notifications_enableddirect_message_notifications_enabledgroup_message_notifications_enablednotification_previews_enabled
Enable web push notifications on the panel:
use Wirechat\Wirechat\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->webPushNotifications();
}
Use the same settings DTO in custom notification code:
use Wirechat\Wirechat\Facades\Wirechat;
$settings = Wirechat::settings($recipient);
$canNotify = $settings->notifications_enabled
&& ($conversation->isGroup()
? $settings->group_message_notifications_enabled
: $settings->direct_message_notifications_enabled);
Read more on the Notifications page.
Configurable Models
The config model map now includes the new records used by settings, message requests, group invites, and join-request flows:
'models' => [
'invite' => \Wirechat\Wirechat\Models\Invite::class,
'join_request' => \Wirechat\Wirechat\Models\JoinRequest::class,
'message_request' => \Wirechat\Wirechat\Models\MessageRequest::class,
'setting' => \Wirechat\Wirechat\Models\Setting::class,
],
Each custom class must extend the matching Wirechat base model. Use this when you need app-specific relationships, scopes, observers, or helper methods while keeping the package schema contract intact.
Read more on the Models page.
Upgrade Checklist
- Update
wirechat/wirechatto^0.6. - Publish and run the new migrations.
- Review any published Wirechat views before replacing them.
- Add
invite,join_request,message_request, andsettingkeys to customized config files if they are missing. - Enable only the panel features your application needs.
- Test private chats, pending message requests, group invitations, join requests, and notification preferences before deploying.
Laravel News Links
