The current iteration of our Tumbleweed Can-O-Worms suffers from leakage down the legs:

Perhaps surprisingly, the whole process is odorless. This is in the basement where ugly doesn’t count for much.
The leakage comes through the air vent holes at the top of each leg socket:

Unsurprisingly, the “worm tea” (it ain’t water!) dripping from the bottom of the compost tray goes directly through the vent holes and down the leg. It’s not clear to me what they expected.
A simple 3D printed black TPU cap atop the leg socket should prevent most of the leakage:

The odd pattern comes from Concentric infill around the notch, even though Concentric infill seems better suited to round things and fat extrusion threads with a 0.8 mm nozzle than straight lines. The weird stuff on the vertical side is a reflection of the basement and phone flash from the smooth TPU layer edges.
The tray with the worms and compost sits atop the strut between the socket and the body, with the cap allowing room for the screen keeping the worms out of the sump.
Ribs on the underside of the cap hold it above the leg socket to let the air through:

Although five caps fit the MK4’s platform, I printed one or two at a time while tweaking improving the design:

This design is for the original Can-O-Worms shown in the second picture and may not quite fit the new version. We’ll know more when we empty the bottom tray.
The OpenSCAD source code as a GitHub Gist:
| // Worm Bin Leg Vent Cover | |
| // Ed Nisley – KE4ZNU | |
| // 2026-10-16 | |
| include <BOSL2/std.scad> | |
| /* [Hidden] */ | |
| ID = 0; | |
| OD = 1; | |
| LENGTH = 2; | |
| HoleWindage = 0.2; | |
| Protrusion = 0.01; | |
| NumSides = 8*2*3; | |
| $fn=NumSides; | |
| LayerHeight = 0.4; // print with 0.8 mm nozzle | |
| LayerWidth = 0.9; | |
| WallThick = 1.5*LayerWidth; | |
| VentOD = 62.0; // at bottom of cover | |
| VentTopClear = 9.0; // to next layer bottom | |
| VentSideClear = 11.0; // to bin side | |
| VentStrutWidth = 3.0; | |
| CoverHeight = 15.0; | |
| CoverOD = VentOD + 2*VentSideClear – 2*WallThick; | |
| NumRibs = 6; | |
| RibHeight = VentTopClear – WallThick – 2.0; | |
| RibThick = 2*WallThick; | |
| //—– | |
| // Build things | |
| difference() { | |
| union() { | |
| cyl(WallThick,d=CoverOD,circum=true,anchor=BOTTOM) position(TOP) { | |
| tube(CoverHeight – WallThick,od=CoverOD,wall=WallThick,circum=true, | |
| anchor=BOTTOM,spin=180/NumSides); | |
| for (i=[0:NumRibs-1]) | |
| rotate(i*(360/NumRibs) + 180/NumRibs) | |
| right(CoverOD/3 – RibThick/2) | |
| cube([CoverOD/3,RibThick,RibHeight],anchor=BOTTOM); | |
| } | |
| up(WallThick) | |
| right(CoverOD/2 – WallThick) | |
| cube([VentSideClear,VentStrutWidth + 2*WallThick,RibHeight],anchor=BOTTOM+RIGHT); | |
| right(CoverOD/2 – WallThick + Protrusion) | |
| cube([VentSideClear – WallThick,VentStrutWidth + 2*WallThick,CoverHeight],anchor=BOTTOM+RIGHT); | |
| } | |
| down(Protrusion) | |
| right(CoverOD/2 – VentSideClear) | |
| cube([CoverOD,VentStrutWidth,2*CoverHeight],anchor=BOTTOM+LEFT); | |
| } |
Spam comments get trashed, so don’t bother; you’re not as clever as you think. Comment moderation may cause a delay.