Skip to content

Releases: myastroboard/myastroshine

v0.6.1

Choose a tag to compare

@github-actions github-actions released this 23 Oct 22:13
ec6aeaa

What's changed

Fixes

  • The backend HTTP client moves from the frozen httpx to its maintained fork httpx2 (Astrodex
    handoff, update check); HTTPS now verifies against the system certificate store.
  • Security: Pillow moves to 13.0.0, fixing the 15 security advisories pip-audit reports
    against 12.3.0, and every other backend and frontend dependency is brought to its latest release.

Commits since v0.6.0

  • chore: file changelog for release v0.6.0 (192530b)
  • chore: sync organization standards (a50e604)
  • chore: update dependencies in package.json (3b16ca0)
  • fix(deps): replace frozen httpx with its maintained fork httpx2 (a246be6)
  • chore: sync organization standards (#68) (d6bf4c1)
  • Fix/deps pillow 13 (#71) (ec6aeaa)

Docker image

docker pull ghcr.io/myastroboard/myastroshine:0.6.1       # GitHub Container Registry
docker pull myastroboard/myastroshine:0.6.1  # Docker Hub
Platform Architecture
linux/amd64 x86_64
linux/arm64 ARM 64-bit (Raspberry Pi 4/5, Apple Silicon)

v0.6.0

Choose a tag to compare

@github-actions github-actions released this 07 Oct 10:16

What's changed

Features

  • An iPhone ProRAW (.dng) opens in the linear editor, and a Milky Way shot with a landscape uses
    the phone's sky mask, single or stacked: no halos round trees, a dark night sky. See
    docs/ALGORITHMS.md "Nightscapes".
  • Wide-angle shots (35 mm-equivalent focal up to 50 mm) have their lens vignetting and sky colour
    shading corrected automatically, and a clean stack no longer crushes darker sky to black.
    Phone ProRAW keeps the phone's white balance and stacks with equal weights.
  • Stacking follows wide-angle lens distortion: stars stay sharp across the whole stack instead of
    turning into short streaks toward the corners.
  • New optional "Style" step before Export: Vivid, Soft glow or Cinematic finishing looks from a
    gallery of your own image, one strength slider, nothing invented. See docs/ALGORITHMS.md "Looks".
    The Astrodex copy carries the style, and its parameters gain a look key (myastroboard).
  • Night landscapes get two more styles that treat the sky and the ground apart: Galactic core and
    Blue hour, with no halo round the trees. See docs/ALGORITHMS.md "Looks".
  • Deep-sky styles, picked with chips above the gallery: Luminous and Structure for nebulae, Deep
    field and Warm core for galaxies, Sparkle and Night velvet for star clusters - stars keep their size.
  • Moon and planet styles (Crisp and Moonlight, Crisp and Rich colour), sized to the disc, with no ring
    on the limb; after a built-in preset the Style gallery opens on the matching kind of picture.

Fixes

  • Uploading stack frames now shows real progress and a frame counter instead of sitting at 0 % until
    the end; requests stay under the per-file size limit from Settings (so a reverse proxy accepts them).
  • The editor no longer crashes on a phone ProRAW whose capture info has no gain or sensor
    temperature (the info panel now skips fields the API sends as null).
  • Stacking now removes an aircraft or satellite trail from a short stack (3 to 10 frames), and the
    default winsorized rejection no longer leaves a faint line where a trail was rejected.
  • An upload a proxy refuses as too large (Cloudflare caps requests at 100 MB) now says so and
    suggests the local address, instead of showing the proxy's raw HTML error page.
  • Full-resolution renders are about 10% faster (vibrance, denoise and sharpen), with identical
    output. The weekly performance benchmark's full-res budget is now 12 s; see CONTRIBUTING.md.
  • Lib update
  • Auto Astro now showcases the picture instead of crushing its sky to black: a darker but never
    clipped sky, a lifted object, a neutral background, noise and colour dosed per kind of picture,
    and your Stack settings kept. See docs/ALGORITHMS.md "Auto Astro".
  • Stacked composites no longer render with a green sky, and Denoise now also removes the coarser
    grain and colour mottle of real stacks while sparing faint stars. See docs/ALGORITHMS.md.
  • Phone Milky Way shots (wide-angle ProRAW) no longer come out covered in green and magenta blotches,
    and a light-polluted sky no longer gets a dark arch and lavender sides. See docs/ALGORITHMS.md
    "Render hints".

Commits since v0.5.1

  • chore: file changelog for release v0.5.1 (71d4443)
  • fix: improve error handling for uploads exceeding proxy limits (9e23816)
  • chore: update checklist for MyAstroShine 0.5.1 with new features and logging improvements (172997c)
  • chore: sync organization standards (6ee8212)
  • chore: add workflow to require changelog entry for pull requests (0e80d17)
  • chore: update contribution guidelines and changelog format to align with organization standards (caf83a0)
  • Add sky mask functionality and tests for nightscape processing (69f2b72)
  • Enhance star alignment with polynomial warp refinement for wide-angle lens distortion (c7ba45a)
  • Refine devignette and normalised_radius functions for improved vignetting correction and clarity in documentation (d87faa1)
  • Add affine reference coordinates and enhance drizzle integration (d5b9b99)
  • Update source-map-js to version 1.2.2 for improved functionality and security (01029ac)
  • chore: update dependencies in package.json and package-lock.json (f08377e)
  • refactor: update performance benchmarks and enhance full-resolution rendering speed (8a3a618)
  • feat: add style feature for image editing with localization support (10494e9)
  • feat: add new night landscape looks and enhance style panel functionality (8479e2d)
  • Enhance style gallery with new looks and grouping (3c8ecfa)
  • feat: add Moon and Planet looks to the style gallery (9aa341c)
  • feat: enhance upload process with progress tracking and size limits (4ef181e)
  • Refactor tests and enhance Auto Astro functionality (e034f15)
  • refactor: update documentation references to align with new structure (d94fe55)
  • feat: improve Milky Way rendering and background extraction for wide-field shots (8415f83)
  • chore: update version to 0.6.0 (ca53343)

Docker image

docker pull ghcr.io/myastroboard/myastroshine:0.6.0       # GitHub Container Registry
docker pull myastroboard/myastroshine:0.6.0  # Docker Hub
Platform Architecture
linux/amd64 x86_64
linux/arm64 ARM 64-bit (Raspberry Pi 4/5, Apple Silicon)

v0.5.1

Choose a tag to compare

@github-actions github-actions released this 01 Oct 15:39

What's changed

Fixed

  • Clean stop exit code. Stopping the container now exits with code 0
    instead of 143. uvicorn re-raised the SIGTERM after its graceful shutdown, so
    Home Assistant reported the app as "did not handle SIGTERM" and showed an
    error on the app card.

Commits since v0.5.0

  • chore: file changelog for release v0.5.0 (5ab839e)
  • chore: restore CODEOWNERS file with maintainers information (86a34d0)
  • fix: ensure graceful shutdown exits with code 0 instead of 143 (01d34e4)
  • chore: update version to 0.5.1 (53cfa61)

Docker image

docker pull ghcr.io/myastroboard/myastroshine:0.5.1       # GitHub Container Registry
docker pull myastroboard/myastroshine:0.5.1  # Docker Hub
Platform Architecture
linux/amd64 x86_64
linux/arm64 ARM 64-bit (Raspberry Pi 4/5, Apple Silicon)

v0.5.0

Choose a tag to compare

@github-actions github-actions released this 01 Oct 14:11

What's changed

Changed

  • A friendlier editor for people who are not photographers. The start
    screen offers two drop zones side by side (one photo / several shots of the
    same target) instead of a mode toggle. The workflow rail groups the steps into
    Prepare / Enhance / Finish with an icon and a plain-language subtitle each;
    every step opens on one short sentence with the details behind "Learn more",
    and ends on a distinct "Next step" button. Auto Astro is the Start step's main
    action and confirms when it has set everything; presets are cards with an icon
    and a visible description. Sliders fill from their neutral value, white
    balance / tint / exposure / colour show a gradient track, and a double-click
    puts a slider back to neutral. Before / After are labelled on the image, the
    histogram is folded away, and Export ends on a "your image is ready" recap.
    Several labels say what they do ("Clean the sky", "3D effect", "Remove
    haze"). Own line-icon set, no emoji; footer theme toggle as icons.

  • Go-back points (formerly milestones) explain themselves, are named after
    the steps that changed ("2 - Light, Colour"), and going back from an unsaved
    edit keeps that edit as a new point first - nothing is lost.

  • Phone layout: the preview is pinned above the controls and the rail is a
    bottom tab bar; the editor no longer overflows horizontally.

  • One container, no Redis, no worker. Image and stack processing now run as
    background jobs inside the application process, and the app runs its own
    maintenance (hourly cleanup, minute-by-minute watch-folder poll). /process
    and /stack/{id}/process always answer at once with the queued job; progress
    streams over the same WebSockets. Edits and stacks have separate thread pools,
    so a long stack never blocks the editor. A job still running at shutdown stops
    at its next step and ends failed (an external engine it ran is killed, not
    orphaned); jobs left over from a crash are failed at the next start. The
    star-mask, Auto Astro, depth-shift and upload-decoding routes no longer run on
    the event loop. Upgrading from compose: take the new docker-compose.yml
    (or drop the worker and redis services) - see docs/DEPLOYMENT.md.

  • Runs on older x86-64 CPUs. The amd64 image now starts on CPUs without
    x86-64-v2 (Proxmox's default kvm64 CPU model, used by the Home Assistant OS VM
    scripts), where NumPy's PyPI wheels crashed with "Illegal instruction": NumPy
    is rebuilt without a CPU baseline. A CPU compatibility CI check (also a gate
    of the release) runs the image under an emulated kvm64 CPU.

  • Listens on IPv4 and IPv6. One dual-stack socket instead of IPv4 only, so a
    host name resolving to IPv6 (often homeassistant.local) reaches the app.

  • Clean, bounded stop. A container stop ends in a few seconds with exit code
    0, even with a job running (it ends failed, "interrupted by a server
    shutdown").

  • Uploads never sit in memory whole. Files stay in their spooled temp files
    and are read one at a time: a 20-frame batch, a calibration set or a .zip
    archive no longer loads entirely into RAM (an archive was read whole before),
    which matters on small hosts like a Raspberry Pi. Oversized files are refused
    from their declared size before they are read, an over-cap request before its
    body is received, and an archive member before it is unpacked. Batch routes
    take at most 20 files per request; the web UI now also sends calibration
    frames in batches of 20.

  • Works under a path prefix. The web UI builds every URL - assets, API calls,
    images returned by the API, WebSockets - relative to the page, so it can be
    served at https://example.com/astro/ behind a reverse proxy or inside a Home
    Assistant ingress. CI checks the source and the built bundle for root-absolute
    URLs, and an e2e run walks the app behind a fake ingress proxy.

  • Stack step: adaptive, colour-preserving stretch (stack.stretch_mode,
    default "adaptive"). The stretch now works on luminance and carries colour
    as a ratio, and its curve is solved per image from the sky level and the
    object level (both read on a star-suppressed copy). Galaxy cores are no longer
    clipped flat (M31's core was never saturated in the linear data - the old
    stretch clipped it), stars keep their colour, and a frame-filling nebula is no
    longer mistaken for the sky. "classic" keeps the previous stretch.

  • Colour calibration uses the star field as the white reference. Aperture
    photometry on the unsaturated stars sets the channel gains, replacing the
    balance of channel means, which a strongly coloured target (a red emission
    nebula, the Pleiades) pulled toward grey. Falls back to the previous method
    when fewer than 20 clean stars are measured. The sky is neutralised without
    clipping its noise, which removes the purple, mottled background a weak-blue
    sensor gave after calibration.

  • Denoise, colour noise reduction and sharpness are multiscale (starlet
    wavelet) operations
    instead of a bilateral filter and a 3x3 Laplacian kernel:
    grain is removed while stars and filaments keep their amplitude, and
    sharpening only boosts structure that rises above the noise.

  • Saturation and vibrance run in float and preserve luminance (no 8-bit HSV
    round trip).

  • StarNet2 and DeepSNR exchange 16-bit or float data, never 8-bit. On a
    stacked composite, DeepSNR now runs on the linear data before the stretch (a
    float FITS with --linear), which is what the model is trained on. The
    engine estimate cache stores lossless .npy files.

Added

  • Ready for Home Assistant ingress. Running as a Home Assistant app (the
    Supervisor injects SUPERVISOR_TOKEN), requests from the Supervisor get the
    browser's own address back from X-Forwarded-For, so the rate limit, the
    login throttle and the concurrent-job cap count each HA user apart instead of
    all of them as one; the admin cookie is scoped to the ingress prefix and
    Secure when HA is served over HTTPS. The Supervisor's headers are honoured
    only from its address (IPv4-mapped forms included) and removed from anyone
    else's requests; nothing changes for a plain Docker install.
  • Reset the admin password without a shell. An empty reset-admin file in
    the data directory - for the Home Assistant app, in its folder under
    addon_configs - clears the admin password at the next start and is deleted.
    The login form says how.
  • Admin password. Settings - and every /api/admin/* and /api/tokens
    route - now need an admin login; using the app (upload, edit, stack, download,
    presets) stays open. On first start the Settings page asks to create the
    password; until then the administration surface is locked. Settings ->
    Security changes the password, lists the logged-in browsers (and logs them
    out), and sets how long a login lasts (admin_session_idle_days, default 7,
    30 at most). Failed logins are throttled per IP. A lost password is reset from
    the host with python -m app.cli reset-admin. New routes under /api/auth
    (docs/API.md "Admin authentication"); migration 0a1b2c3d4e5f adds the
    admin_credentials and admin_sessions tables.
  • Settings reorganised by subject: General, Stacking, ML engines, Astrodex
    (was Webhooks), Security, Logs, Maintenance (was Operations). The catch-all
    Advanced tab is gone: CORS and rate limiting moved to Security, log levels to
    Logs, the ML engines to their own tab, configuration backup to Maintenance.
  • Install StarNet2 / DeepSNR from Settings. Upload the CLI package downloaded
    from starnetastro.com (Settings -> ML engines): the server
    unpacks and checks it, shows its licence, and installs it once the admin
    accepts - no bind mount or path to type. Remove deletes it. A package built for
    another architecture (the x86-64 build on a Raspberry Pi) is refused with a
    clear message. Mounting a binary and setting its path still works. New admin
    routes under /api/admin/engines (docs/API.md "External ML engines").
  • green_removal (0-100): SCNR average-neutral green removal, in the Sky step.

Removed

  • PROCESSING_MODE, REDIS_URL and CELERY_BROKER_URL (ignored if still set),
    the worker and redis compose services, worker.log, and the redis field
    of GET /api/health. celery, redis and celery-types leave the
    dependencies.

Fixed

  • The Depth Shift viewer opened underneath the inspector column (the modal was
    rendered inside the sticky preview column's stacking context).
  • Ellipses and middle dots in the translations are ASCII now ("...", "-").

Dependencies

  • Backend: SQLAlchemy 2.0.54 -> 2.1.1, uvicorn 0.53.0 -> 0.54.0, ruff 0.16.8 ->
    0.16.9 (dev).
  • Frontend (dev): vite 8.3.0 -> 8.3.1, vitest / @vitest/coverage-v8 5.0.1 ->
    5.0.2, @types/node 26.6.2 -> 26.6.3.
  • Backend: fastapi 0.141.1 -> 0.142.2.
  • Frontend (dev): vitest / @vitest/coverage-v8 5.0.2 -> 5.0.3, typescript-eslint
    8.70.1 -> 8.71.0, globals 17.12.0 -> 17.13.0; brace-expansion 5.0.12 (transitive, via eslint) for
    GHSA-q2hr-2g5m-vwhr / GHSA-qhr7-859c-m2p7 / GHSA-6j4f-fj2g-mc7p.
  • Frontend (dev): vite 8.3.1 -> 8.3.2.

Commits since v0.4.2

  • chore: file changelog for release v0.4.2 (ee2d832)
  • Add unit tests for hooks and WebSocket client; enhance coverage configuration (26c1f32)
  • chore: update dependencies in frontend package.json (9fbc6e5)
  • feat: adaptive colour-preserving stretch and multiscale processing (de0e99e)
  • Add unit tests for StackView, adminAuth, appUrl, relativeUrls, and useAdminAuth (29e14c5)
  • feat: add EngineInstaller component for managing external ML engines (b4f35fe)
  • refactor: reorganize settings and update documentation for clarity (8591bfb)
  • fix: standardize spelling of "Astrodex" across the application (bb6a10c)
  • feat: implement CPU compatibility checks and dual-stack server support (32a2a45)
  • fix: clarify socket binding co...
Read more

v0.4.2

Choose a tag to compare

@github-actions github-actions released this 11 Sep 15:14

What's changed

Added

  • A capture info panel for a stacked composite session. Below the workflow
    rail, a small "Capture" card shows what's read off the source FITS
    header(s) - object, telescope, filter, frame count x exposure, total
    exposure, date, gain, sensor temperature - for both an in-app multi-frame
    stack and a single already-stacked upload (a Seestar / ASIAIR live stack).
    An already-stacked source's own frame count / total exposure (STACKCNT /
    TOTALEXP) are trusted as-is; the in-app stacker sums each kept sub's own
    exposure instead. Every field is best-effort and simply omitted if absent;
    the whole card stays hidden for an ordinary photo. New
    GET /api/session/{id}/capture-info.
  • A golden-image regression test suite (backend/tests/regression/) runs a
    synthetic frame through the real enhancement and stacking pipelines on every
    pytest run and diffs the result against a checked-in reference - catches a
    silent numeric regression (a gain, a clamp) that runs cleanly with no
    exception but changes what the pipeline actually produces, the shape of the
    0.4.1 colour-calibration bug. Internal - no user-facing change; see
    CONTRIBUTING.md.
  • Settings gains an Operations tab. Recent processing jobs (status,
    session/stack, timestamp, error - filterable, paginated, superseded rows
    hidden by default since every debounced slider edit creates one) and a
    disk-usage breakdown of the data volume (images / stacks / database / logs)
    • useful for the folder-watch stacking mode, meant to run unattended for
      hours. A new job_history_retention_hours setting (default 7 days) bounds
      how long a finished job is kept; the hourly cleanup now prunes old rows
      instead of letting the jobs table grow forever. New
      GET /api/admin/jobs, GET /api/admin/disk-usage.
  • GET /api/health now actually checks something. It used to always
    report "database": "connected" regardless of whether the database was
    reachable. It now runs a real query, pings Redis when
    PROCESSING_MODE=queue (skipped entirely in sync mode, where nothing uses
    Redis), and reports the data volume's disk usage.
  • Settings -> Advanced gains a Backup & restore block. Exports the runtime
    settings and every user preset (never the 5 built-ins - each instance
    seeds those itself) as one downloadable JSON file, and restores them from
    one - for moving to a new machine or sharing a preset library. Not sessions
    or images: those are transient by design (session_expiry_hours) and the
    actual deliverable is the downloaded picture, not the working session - the
    whole data volume is already the backup unit for everything else. An
    imported preset whose name already exists on the target instance is skipped,
    never overwritten or renamed. New GET /api/admin/config-export,
    POST /api/admin/config-import.

Changed

  • Auto Astro now also measures colour cast and noise. Previously scoped
    to tone (contrast/exposure/highlights/shadows) and star density; it now
    additionally proposes temperature (from the background sky's own
    blue-vs-red balance, damped to half the measured cast so a genuinely
    coloured target isn't washed out - the same caveat as the Colour
    calibration hint below) and denoise / chroma_denoise (from the sky
    background's own luma and Cr/Cb noise level - colour speckle is usually
    more objectionable than luma noise in a stacked frame). Each stays at its
    default when the measurement is inconclusive or the frame is already close
    to neutral/clean; tint, saturation, sharpness, and colour grading remain
    manual - confidence-limited choices a heuristic has no business making.
    A gradient_reduction heuristic (a fitted brightness trend) was also built
    and checked against a real library of stacked composites and lunar
    photos - and reverted, since it couldn't reliably tell a real
    light-pollution gradient apart from an ordinary asymmetric bright subject
    (an ordinary lunar photo measured in the same range as legitimate-looking
    deep-sky corrections); gradient_reduction stays manual-only. The
    denoise/chroma-denoise scale was also recalibrated against that same
    library - the first cut, tuned only against synthetic noise, capped out
    (over-smoothing real DSO detail along with the sky) on nearly every real
    capture tested. See docs/ALGORITHMS.md "Auto Astro".
  • The Colour calibration hint now says what it can get wrong. It neutralises
    the sky and balances the channels toward a white star field - the right call
    for a sensor colour cast, but it can't tell that apart from a target's own
    real colour, so it can wash out a genuinely blue reflection nebula (the
    Pleiades) or a red emission nebula. The hint now says to turn it off for
    those.
  • The tone/colour/detail sliders step by 0.1, not 0.01. Contrast, exposure,
    highlights, shadows, whites, blacks, clarity, sharpness, vibrance and
    saturation showed a spurious second decimal (1.23) no one could aim for by
    hand - a coarser step (matching the crop tool's straighten slider, already
    0.1) lands on the same clean numbers with a fifth as many stops and reads
    simpler at a glance. Every default (1.0, 0.0, ...) still falls exactly on
    the new grid. The 0-100-range sliders (denoise, star reduction, vignette,
    ...) were already stepping by 1 and are unchanged - this is purely a slider
    granularity change, the stored values are still plain floats.

Commits since v0.4.1

  • chore: file changelog for release v0.4.1 (51b5799)
  • fix: improve colour calibration handling for single stacked uploads and adjust editor step order (79ae993)
  • feat: add capture info panel and API for stacked composite sessions, including metadata extraction from FITS headers (932ba40)
  • fix: adjust slider step values for improved user experience and consistency (a0e1531)
  • feat: add golden-image regression tests for image enhancement and stacking pipelines, including synthetic frame generation chore: update dependencies for frontend and backend, including React and Vite docs: enhance CONTRIBUTING.md with regression testing guidelines (cd0e46b)
  • feat: enhance health check and job management features (574f39a)
  • feat: enhance AutoAstroService with gradient reduction, white balance, and noise handling (a6741bf)
  • fix: remove gradient reduction heuristic from AutoAstroService and update documentation (c470efb)
  • Add Portuguese translations for the application (2b02f4a)
  • chore: update version to 0.4.2 (ab1b737)
  • refactor: remove client_ip from JobSummary and related components (aa4f12b)

Docker image

docker pull ghcr.io/myastroboard/myastroshine:0.4.2       # GitHub Container Registry
docker pull myastroboard/myastroshine:0.4.2  # Docker Hub
Platform Architecture
linux/amd64 x86_64
linux/arm64 ARM 64-bit (Raspberry Pi 4/5, Apple Silicon)

v0.4.1

Choose a tag to compare

@github-actions github-actions released this 10 Sep 11:59

What's changed

Changed

  • A stacked FITS (or a 16-bit PNG/TIFF) now opens as a composite session, the
    same as the multi-frame stacker's own output, instead of a one-shot 8-bit
    auto-stretch on upload. The editor's linear Stack step - background
    extraction, colour calibration, a tunable deep stretch - runs on the 32-bit
    data on every render, and the faint signal is never quantised to 256 levels on
    the way in. A Seestar / alt-az live stack's field-rotation + vignette border
    (the "red top / marked corners") is detected from the pixels and trimmed on
    ingest. POST /api/upload returns is_stack: true for these. See
    docs/ALGORITHMS.md "Upload ingest" and app/services/linear_upload.py.
  • Background extraction gains a frame-edge residual correction. After the
    degree-2 polynomial it adds back the object-free sky residual, smoothed and
    masked to the frame border, so the sharper edge/corner colour cast a
    paraboloid can't bend to (a Seestar footprint the crop only partly removes) is
    taken too - while a galaxy halo or a frame-filling nebula in the interior is
    left exactly as before.
  • A 3-plane (RGB) FITS that still goes through decode_image (an AstroDex
    handoff, say) is stretched with one shared, colour-preserving transform and a
    deep sky target, not three independent per-channel stretches that lifted the
    sky to a milky grey and rebalanced the colour.
  • The white-balance Temperature slider snaps to standard Kelvin stops
    (2000 / 3200 / 4500 / 5500 / 6000 / 6500 / 7000 / 7500 / 8000 K) instead of a
    free 50 K ramp, and the readout names the tone ("Neutral · 6500 K"). Lower K
    warms the image, higher K cools it - the hint said the opposite and is fixed.
    The backend still accepts any value in range; an older preset's off-stop value
    shows verbatim with the thumb on the nearest stop.
  • Each editor slider gets a one-step revert. The value a slider held before
    your current run of edits is remembered, and a small back-arrow appears next
    to that slider (only the one you're adjusting) to put it straight back. Cleared
    by a preset, a section reset, or a milestone restore.
  • Vignette is now signed (-100..100). Positive still brightens the corners
    (lens-vignetting correction); negative darkens them - to deepen a vignette for
    effect, or tame an over-corrected stack.

Fixed

  • A trailing slash on a CORS origin or AstroDex callback allowlist entry
    in Settings is now stripped on save (https://host/ -> https://host). A
    pasted slash previously made the entry silently match nothing - the CORS
    origin never carries one and the allowlist is checked against a slash-stripped
    callback_base. The allowlist match also now enforces a path boundary, so
    https://board.example no longer also authorises https://board.example.evil.
  • Editor sliders and tone curves now render on release, not on a timer.
    Dragging slowly, or pausing mid-drag, no longer kicks off a render before you
    let go - the value tracks live and the pipeline runs on pointer-up / key-up
    (with a 1.2 s fallback if a release event never arrives).

Commits since v0.4.0

  • feat: implement AstroDex handoff integration (01e7dc8)
  • chore: update version to 0.4.0 and reflect in README (19c346f)
  • chore: file changelog for release v0.4.0 (61ebc98)
  • fix: update image references in docker-compose and deployment documentation (19dcac2)
  • feat: implement crop_low_signal_border for single uploaded stacks (3ec95ba)
  • feat: add frame-edge residual correction to background extraction (2a1f6c0)
  • fix: normalize trailing slashes in CORS origin and AstroDex callback allowlist entries (d003833)
  • feat: implement temperature slider snapping to standard Kelvin stops with updated hints (4f297e3)
  • feat: update slider rendering to trigger on release, enhancing user interaction (3b784d1)
  • feat: implement one-step revert for slider adjustments and update vignette correction range (4007306)
  • chore: bump version to 0.4.1 (3b03033)

Docker image

docker pull ghcr.io/myastroboard/myastroshine:0.4.1       # GitHub Container Registry
docker pull myastroboard/myastroshine:0.4.1  # Docker Hub
Platform Architecture
linux/amd64 x86_64
linux/arm64 ARM 64-bit (Raspberry Pi 4/5, Apple Silicon)

v0.4.0

Choose a tag to compare

@github-actions github-actions released this 09 Sep 16:05

What's changed

Changed

  • AstroDex integration rebuilt around a signed handoff. MyAstroBoard now
    opens the editor with a single ?handoff= token; the backend verifies it,
    pulls the source image from the board itself, and drops you straight into the
    editor. The Export step gains Send back to AstroDex, which posts the
    processed image back as a signed multipart/form-data request - the board
    files it as a new picture on the same object, never a replacement. This
    instance is never contacted by the board, only ever calls out to it, so
    the flow works whether the board is on the LAN or behind a reverse proxy. The
    handoff token is HMAC-SHA256 signed with a webhook token's signing_secret,
    expires after 12 h, and is single-use for the return. See docs/API.md
    "AstroDex integration" and initial_plan/PASSATION_MYASTROBOARD_INTEGRATION.md.

Removed

  • The never-wired push endpoints POST /api/astrodex/receive and
    POST /api/send-to-astrodex (and the AstroDexService / AstroDexDispatch
    services), replaced by the handoff routes above. astrodex_links is reshaped
    by migration d4e5f6a7b8c9; astrodex_callback_urls /
    astrodex_max_retries / astrodex_retry_delay_seconds are unchanged.

Commits since v0.3.0

  • Add Docker Image Size badge to README (f10be9b)
  • chore: file changelog for release v0.3.0 (3888d70)
  • feat: implement AstroDex handoff integration (237f116)
  • chore: update version to 0.4.0 and reflect in README (b50d2e4)

Docker image

docker pull ghcr.io/myastroboard/myastroshine:0.4.0       # GitHub Container Registry
docker pull myastroboard/myastroshine:0.4.0  # Docker Hub
Platform Architecture
linux/amd64 x86_64
linux/arm64 ARM 64-bit (Raspberry Pi 4/5, Apple Silicon)

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 09 Sep 09:01

What's changed

Added

  • Star removal (starless): the editor's Stars step now offers two ways to
    handle stars, grouped and explained side by side - Reduce (shrink each
    star in place, the existing control) and Remove. Raise Remove stars
    and the pipeline splits: the stars are pulled out right after the background
    corrections, every creative adjustment (tone, curves, colour, detail) then
    works on the nebula alone, and Bring stars back screen-blends them in at
    the end (0 keeps the image starless, 100 restores them full strength).
    Detection (sensitivity / max size, mask preview) is shared by both. The
    reconstruction is classical - a median-blur estimate on a downscaled copy,
    sized so galaxy and nebula cores are preserved - with no ML model and no new
    dependency (it also let scikit-image, unused since the v0.2 star-detection
    rebuild, be dropped). A very dense Milky Way star field is the known limit of
    the classical method; the StarNet2 engine below is the way past it.
  • Optional ML engines for star removal and noise reduction, installed by the
    operator, never bundled
    . Point Settings -> Advanced at a
    StarNet2 and/or DeepSNR binary you mount into the
    containers and the editor gains an engine toggle: StarNet2 in the Stars
    step (per-edit, replaces the classical split for the run) and DeepSNR in
    the Detail step's denoise control (runs early, before the tone stretch). Each
    is arm's-length - MyAstroShine writes a TIFF, runs the tool, reads the result
    back - and any failure (missing binary, timeout, bad output) logs and falls
    back to the classical code, so a client can always request them. A pass streams
    its progress onto the editor bar; the result is cached per session so a
    creative-only tweak doesn't re-run it. Nothing ships in the image and no
    Python dependency is added - see docs/DEPLOYMENT.md "External ML engines" and
    THIRD_PARTY.md (the tools are non-commercial; the operator accepts their
    terms).
  • Edit milestones: a timeline under the preview where you can save the current
    adjustments (sliders, curves, framing, and the Depth Shift focal point) as a
    checkpoint and click back to it if you push an edit too far. The first
    milestone is always the original image. Milestones live only in the browser
    for the image being edited - loading another photo starts over, and there is
    no server-side copy.
  • Light theme, now the default, matching the MyAstroBoard visual charter (deep
    teal primary accent, amber ecosystem accent, frosted panels over a lightly
    teal-tinted ground). The dark theme is unchanged.
  • A Theme control in the footer - System / Light / Dark. "System" follows the
    operating system's light/dark setting and updates live when it changes. The
    choice is remembered per browser.
  • The upload screen now shows real progress: a percentage and bar while the file
    transfers (over XMLHttpRequest, which fetch can't report), then an
    indeterminate "Preparing your image…" spinner while the server decodes it -
    previously a large FITS / RAW upload just sat with an unchanged button.
  • GET /api/config - a small public endpoint exposing the runtime limits the UI
    needs before a session exists (the upload size cap, stacking limits). The
    upload screen's size hint and its pre-flight check now follow the operator's
    configured max_image_size_mb instead of a hardcoded 100 MB.
  • Stacking rebuild (in progress): multi-frame stacking is being reworked to run
    on linear, full-bit-depth data instead of stretched 8-bit previews - the old
    pipeline was never run on real data and produced a near-black result on a real
    Seestar set. This release lays the foundation: a unified linear ingest (FITS
    Bayer mosaics kept intact and debayered in-pipeline, camera RAW demosaiced
    linearly, 8-bit previews sRGB-linearised), a .zip archive upload so a
    thousand-frame session is a handful of requests, per-frame thumbnails, and a
    frame-by-frame include/exclude toggle (POST /api/stack/{id}/frame/{i}/exclude)
    for dropping a sub with a trail or cloud. Frames now upload in batches
    (POST /api/stack/{id}/upload-frames) or as a .zip
    (POST /api/stack/{id}/upload-archive) instead of one request per frame, and
    the frontend checks the frame count against the instance limit before starting
    so you get a clear message, not a raw error. The stacking screen is reworked
    into a collect -> upload -> review -> stack flow: a drag-and-drop zone matching
    the single-image one (accepted formats, size limit), then frames upload as a
    batch and a thumbnail grid lets you click any frame to preview it large and
    tick a box to drop a trailed or clouded sub. After stacking you can still tweak
    a setting or exclude another frame and hit Re-stack
    (POST /api/stack/{id}/process now takes an optional settings body) with no
    re-upload, or inspect a frame and go "Back to composite". A beforeunload
    guard stops a mis-swipe or a mouse "back" from losing an in-progress stack.
    The pipeline now registers the frames - it detects stars, picks the
    sharpest frame as the reference and asterism-matches the rest onto it (a
    vendored triangle matcher, no new dependency), warps them with Lanczos,
    normalises each to the reference, and combines with iterative sigma rejection
    and noise weighting, all in bounded memory (a float16 memmap + row tiling,
    so a thousand-frame stack does not need a thousand frames of RAM). On a real
    150-frame Seestar set: sub-pixel alignment, ~8.7x noise reduction. It now also
    calibrates each frame: upload master darks, flats, bias and dark-flats
    (POST /api/stack/{id}/calibration/{kind}/frames) and the pipeline builds a
    median master of each, applies (light - bias - dark) / flat on the Bayer
    mosaic before debayer, and repairs hot / dead pixels from the master
    dark/flat with a neighbour median (the honest replacement for the removed
    "cosmic ray" step; toggle with Repair hot & dead pixels). The Bayer
    mosaic is now debayered with an edge-aware interpolating demosaic at full
    resolution for the align/combine passes (registration still measures on the
    cheap half-res superpixel). Every sub is now scored (star count, FWHM,
    roundness, sky background, SNR) and the worst are auto-rejected: the
    Auto-reject bad frames setting (off / lenient / moderate / strict, default
    moderate) drops a clouded, soft, trailed or dawn sub before it can pollute the
    stack, the frame grid shows each sub's score and why it was dropped, and
    unchecking a frame rescues it from the filter on the next run. weighting: "quality" now uses that same score. The finished composite is cleaned up
    before it opens in the editor (Clean up the composite, on by default): the
    field-rotation edges are cropped away, a low-order sky-gradient is subtracted
    (a degree-2 fit that cannot carve into a nebula), and the colour is
    neutralised and balanced. Combined with a better reference-frame choice (the
    middle of the session, not the frame with the most stars) a real 100-sub set
    now comes out framed on the target with a flat neutral background instead of
    noisy rainbow speckle. Finally, a stacked composite now opens the editor on
    its 32-bit linear data, with a new Stack step (composite sessions
    only): a Stretch control that sets the auto-stretch strength, a
    Background extraction slider, and a Colour calibration toggle - all
    non-destructive, recomputed from the linear composite so nothing is lost to an
    early 8-bit quantisation. The whole creative pipeline (ImageProcessingService)
    now runs in float32 internally and quantises only at the encode boundary, which
    also benefits single-image FITS/RAW edits. Only the field-rotation crop is
    baked into the saved composite; background extraction and colour calibration
    moved out of the one-shot cleanup and into the Stack step.
  • Stacking scale & UX: the progress bar now shows which frame each step is on
    ("Aligning 340 / 1066"), not just a percentage. The register / align / combine
    passes run on a thread pool (auto-sized to the CPU count, capped at 4,
    tunable in Settings) - roughly 2x faster on a real multi-frame stack, and
    parallelising the combine step is what moved the needle. An interrupted run now
    resumes: the aligned frames and a small plan file survive a crash, so a
    retry - or a re-stack that only changes the combination / rejection / weighting
    • skips straight to the combine step instead of re-registering everything. And
      a watch folder: point the worker at a directory (Settings) and frames
      dropped there are ingested into a rolling stack that auto-processes once the
      folder goes quiet; the stacking screen shows a banner to open it.
  • Drizzle (Fruchter & Hook) as an opt-in stacking option (2x / 3x). Each
    input pixel is a shrunken "drop" mapped through its registration transform and
    area-distributed onto a finer output grid, so a large, well-dithered stack
    (the Seestar's alt-az field rotation supplies the dither) yields real
    resolution gain a plain resample cannot. It runs as an extra pass after the
    normal stack (which stays the fallback and the outlier reference), is slower
    and correlates neighbouring-pixel noise, and is off by default.

Changed

  • The language switcher moved from the header to the footer, next to the new
    theme control; the header now carries only the brand and Settings.
  • Denoise and Chroma denoise now use the documented bilateral-filter range
    (diameter 5-20, sigma 75-150) so high slider values deliver the intended
    stronger noise cleanup on deep-sky frames.
  • Built-in deep-sky presets were rebalanced to darken shadows instead of
    lifting them (Galaxy shadows: -0.25, Deep Field shadows: -0.35) for
    better object-vs-background separation, matching the Auto Astro strategy.
  • The pe...
Read more

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 06 Sep 09:58

What's changed

Added

  • Star reduction rebuilt on per-star detection (StarDetectionService, a
    thresholded top-hat + cv2.connectedComponentsWithStats, at native
    resolution, robust to ordinary sensor/JPEG noise) instead of a single
    image-wide top-hat mask, so only genuine stars are shrunk and diffuse
    nebulosity is never dimmed. Each star shrinks via erosion floored at its
    own local background, so it can't crush to a black dot or bloat into a flat
    pale disc at full strength. New star_sensitivity / star_max_size
    parameters tune what counts as a star.
  • Star mask preview: POST /api/star-mask/{session_id} reports detected star
    positions for a live "N sources detected" overlay in the editor, toggled from
    the Stars panel.
  • Auto Astro: a one-click "Auto Astro" button (POST /api/auto-astro/{session_id})
    analyses the image's histogram and star density and applies a computed
    starting parameter set - crushing the background for depth/separation from
    the DSO, and a gentle, log-scaled star reduction that stays a sensible
    starting point across both sparse frames and busy deep-stacked fields -
    in place of manually dialing in every slider. Auto Astro and preset apply
    both keep the session's current framing (crop / rotate / straighten / flip)
    rather than resetting it - a look is not a composition.
  • Editor UX: a workflow-driven three-pane editor. A numbered rail on the
    left steps through the retouching order (1 Framing, 2 Background, 3 Light,
    4 Curves, 5 Colour, 6 Detail, 7 Stars, 8 Depth), bracketed by a
    one-click Start (Auto Astro + presets) and Export - the same order
    the backend pipeline applies. Only the selected step's controls show, in an
    inspector panel with its own help text, reset, and a "Next" link; the
    preview + histogram stay pinned on the right instead of scrolling out of
    view under a long list of tools. A step shows a dot on the rail once its
    values leave the default. Crop / straighten / rotate / flip is now step 1,
    edited directly on the preview (the separate full-screen crop editor is
    gone). White balance (temperature / tint) moved from the creative colour
    controls into step 2, next to the other background corrections, matching the
    pipeline. A focal-point picker on the preview (click to set) drives where
    the Depth Shift parallax centers - focus_point was accepted by the API
    before this but never used.
  • A "New photo" button on the editor returns to the upload screen -
    previously the only way back was a full reload. Leaving with unsaved edits
    (the button, or a browser back / reload / mobile edge-swipe) now asks first,
    so a mis-swipe on a slider no longer silently loses the work. The mode
    switcher is hidden while a photo is open.
  • i18n: FR + EN. Every frontend-owned UI string now goes through
    useTranslation()'s t(), backed by frontend/src/i18n/translations/{en,fr}.json
    (en.json is the reference language); a compact EN/FR selector sits in the
    header. Detects the browser language on first load, then persists the
    choice client-side. scripts/validate_i18n.py (new CI job) checks key
    parity, leaf types, and {placeholder} names between the two files.
    Backend-owned detail strings (log lines, specific error text) stay
    English-only, but the five built-in preset names / descriptions and a few
    whole classes of API failure (server busy, temporarily unavailable, 5xx)
    are now shown in the user's language.
  • Permanent page footer (name, version, GitHub link - inspired by
    MyAstroBoard's own footer bar), replacing the version number that used to
    sit in the header. In-app update check folds into it: once a newer GitHub
    release is published, a discreet second line appears with a link to it and
    a "What's new" that opens the release notes in a modal, rendered from
    markdown (headings, bold, lists, links) rather than shown as raw
    ### /** syntax. GET /api/version/check-updates (VersionCheckService)
    queries GitHub's releases API and caches the result in memory for 4 hours,
    so the frontend's 4-hourly poll can never translate into hitting GitHub's
    rate limit; every failure path (timeout, GitHub's own rate limit, a
    malformed response, ...) degrades to no notice rather than an error. The
    frontend re-verifies the version comparison itself before showing
    anything, so a stale/incorrect cache can never present as a downgrade.
  • Tone curve editor: an interactive curve graph (drag points to reshape,
    double-click to add or remove a point) alongside the adjustment sliders.
    Backed by a new curve_points parameter and a monotone cubic Hermite
    spline LUT stage in the processing pipeline, so a handful of dragged
    points make a smooth curve - not a faceted polyline - and can never
    overshoot past a control point's value into crushed shadows or blown
    highlights.
  • Five missing core astro ops, a new Corrections section, and split
    tone controls:
    • Vignette correction - brightens the corners to counteract lens
      vignetting (a generic radial model, not a per-lens calibrated profile).
    • Gradient reduction - flattens smooth background gradients (light
      pollution, sky glow) via a large-blur background estimate, subtracted
      back out.
    • Dehaze - dark-channel-prior haze removal, restoring contrast/colour
      lost to a veiling glow or light-pollution haze.
    • Colour noise reduction - denoises only the Cr/Cb colour channels,
      leaving luma (and its own separate denoise control) untouched -
      colour speckle tolerates far more smoothing than brightness detail does.
    • Whites / Blacks - push the white/black clipping points, narrower
      and more aggressive than the existing Highlights/Shadows (only the true
      near-white/near-black tail moves, not the broader upper/lower-mid
      range) - the usual distinction in most photo editors.
    • Vignette correction, Gradient reduction and Dehaze live in the editor's
      step 2 (Background), applied before any creative grading.
  • FITS + 16-bit + camera RAW upload support - previously 8-bit JPEG/PNG/
    TIFF only (and a 16-bit TIFF/PNG was silently truncated to 8-bit). FITS
    (.fits/.fit/.fts) and camera RAW (.cr2/.cr3/.nef/.arw/.dng/
    .orf/.rw2/.pef/.raf) now decode via astropy/rawpy respectively.
    FITS and a genuine 16-bit TIFF/PNG both get a new auto-stretch (the same
    "screen transfer function" PixInsight/Siril use) instead of a naive bit-
    shift; RAW is demosaiced with the camera's as-shot white balance. See
    docs/ALGORITHMS.md "Upload ingest: FITS / RAW / 16-bit".
  • Colour curves - the Tone Curve panel now has 4 tabs (RGB / Red / Green /
    Blue), each an independent curve on the same curve_points_to_lut
    monotone-spline LUT the master RGB curve already used, applied via
    cv2.split / cv2.LUT per channel / cv2.merge instead of identically to
    all three. For a colour cast a single white-balance gain can't reach at one
    specific tonal range (e.g. a green background-sky cast only in the
    midtones), or deliberate colour grading. New red_curve_points /
    green_curve_points / blue_curve_points parameters (same empty-by-default,
    first-at-0/last-at-255 validation as curve_points), applied right after
    the master curve. See docs/ALGORITHMS.md "Colour curves".

Changed

  • brightness renamed to exposure throughout the API, Auto Astro, and the
    editor UI - no behaviour change, just a name matching the rest of the
    "Exposure/Highlights/Shadows/Whites/Blacks" tone-control family this
    release completes. A saved preset or automation using the old
    brightness key needs updating to exposure.
  • Full-resolution enhance's performance budget raised from 5s to 8s
    (tests/benchmarks/test_processing_speed.py) to account for the 5 new
    pipeline stages above - measured 5.75s on a 24MP frame with every slider
    active at once (an unrealistic worst case; the interactive preview budget,
    what a slider drag actually feels like, is unchanged and unaffected).

Removed

  • The denoise_enable_ml / depth_detection_method Settings fields (and
    their "Processing" section) - both were placeholders for ML backends
    evaluated this release and not adopted (see docs/ALGORITHMS.md "Depth
    map" and "ML denoising"): no viable pretrained denoising model exists to
    point a self-hoster at, and MiDaS/DPT-Hybrid depth estimation produces a
    near-featureless result on real astrophotos regardless of model size.
    Neither field ever did anything - depth_detection_method had no reader in
    DepthMapService/DepthShiftService even before this evaluation. An old
    app_settings.json with these keys still loads fine; they're just ignored.
  • The depth_shift_intensity processing parameter and its unused
    ProcessRequest.apply_depth_shift companion - dead since they were added
    (no pipeline stage ever read them; the Depth Shift viewer has always kept
    its own intensity state). ProcessingParameters now silently drops the
    retired key on the way in, so a stored preset from an older version still
    loads instead of hitting extra_forbidden.
  • Dead-code sweep before the release: the unused useAstroDexIntegration
    hook is now wired to the "Send to AstroDex" button (which gains
    sending / sent / error feedback); the unused react-router-dom dependency
    (routing is hand-rolled hash-based), the unreachable UpstreamUnavailableError
    exception, the never-called StorageService.count_stack_frames, the unused
    API_PORT constant, and the unused get_astrodex_service DI wrapper are gone.

Fixed

  • A processing job that got stuck non-terminal (a worker died mid-run, or a
    queued job was never picked up) counted against the per-IP concurrency limit
    forever - a few stuck rows would eventually make every enhance / Auto Astro
    fail with "Too many concurrent processing jobs". Such a job now stops
    counting once it is older tha...
Read more

v0.1.0

Choose a tag to compare

@github-actions github-actions released this 04 Sep 19:08

What's changed

First implementation pass covering the full v1.0 + v1.1 roadmap. Nothing has been
tagged yet.

Added

Core enhancement (Sprint 1-3)

  • Image upload with format/size validation, 512 px preview generation, per-channel
    histogram, and session tracking (POST /api/upload, GET /api/preview/{id}).
  • ImageProcessingService pipeline: geometry (rotate / flip / straighten /
    crop), white balance (temperature/tint), contrast, brightness,
    highlights/shadows recovery, saturation, vibrance, clarity, denoise
    (bilateral), star reduction (top-hat star mask + morphological erosion),
    sharpness. Validated against documented bounds.
  • POST /api/process/{id} applies parameters and returns a job handle.
  • GET /api/health system health check.
  • Alembic migration scaffolding; init_db() creates tables for local/dev use.

Job queue and progress (Sprint 3)

  • PROCESSING_MODE setting: sync (run in the request, default) or queue
    (Celery task on Redis).
  • Celery app and tasks (task_process_image, task_process_stack,
    task_cleanup_sessions); task_always_eager under APP_ENV=test.
  • JobRecord tracking with per-step progress; Redis pub/sub bridge.
  • WS /ws/processing-status/{job_id} and WS /ws/stack-status/{job_id}: DB
    catch-up on connect, then live relay until a terminal status.

Presets (Sprint 3)

  • PresetService with five built-ins (Nebula, Galaxy, Deep Field, Lunar,
    Cluster); built-ins cannot be deleted.
  • GET/POST /api/presets, DELETE /api/presets/{id},
    POST /api/presets/{id}/apply/{session_id}.
  • Duplicate-name and quota (50 user presets) guards.

Depth Shift (Sprint 4)

  • DepthMapService: Sobel-gradient depth map, normalization/smoothing, depth
    statistics.
  • Parallax layer generation (2-12 BGRA layers, far to near).
  • POST /api/depth-shift/{id}, plus metadata, depth-map, and per-layer endpoints.

AstroDex integration (Sprint 4)

  • AstroDexService: canonical-JSON HMAC-SHA256 signing, payload composition,
    retry with exponential backoff on 5xx.
  • POST /api/astrodex/receive (inbound image) and POST /api/send-to-astrodex
    (outbound signed webhook, delivered in the background).
  • Long-lived bearer webhook tokens with per-token signing secrets, created and
    revoked from the Settings UI (GET/POST /api/tokens, DELETE /api/tokens/{id}).
  • Callback-URL allowlist (ASTRODEX_CALLBACK_URLS).

Stacking (Sprint 6-7)

  • RegistrationService: SIFT/ORB keypoints, Lowe ratio test, RANSAC homography,
    perspective warp.
  • NormalizationService (background-level equalization) and CosmicRayService
    (MAD-based robust sigma rejection with an absolute-deviation floor).
  • CombinationService: NaN-aware median, mean, and iterative sigma-clip;
    SNR-improvement estimate (~sqrt(N)).
  • POST /api/stack/initiate, POST /api/stack/{id}/upload-frame,
    POST /api/stack/{id}/process, GET /api/stack/{id}. The composite is a normal
    session and can be enhanced, previewed, and downloaded.

Frontend

  • React 19 + TypeScript + Vite single-page app, Tailwind CSS v4 (CSS-first).
  • "Darkroom / neutre pro" design system (docs/DESIGN.md): semantic token layer
    and shared component classes (.panel, .btn, .field, .slider,
    .segmented, .chip, .dropzone) in src/styles/index.css. Dark-only.
  • Single-image editor: drag-and-drop upload, before/after split preview with zoom
    controls, histogram, grouped parameter sliders (500 ms debounce), preset
    buttons, "save as preset" dialog, download.
  • Parameter tooltips: each slider gets a small "i" badge (hover or keyboard
    focus, screen-reader linked via aria-describedby) explaining what it does.
  • Crop / rotate tool (CropTool): full-screen mode with a draggable crop
    rectangle (corner + edge handles), straighten dial (+/-45 deg), 90 deg rotate,
    horizontal / vertical flip, and aspect presets (Free / Original / 1:1 / 16:9 /
    3:2 / 4:5 / 5:4). Commits a geometry object applied first in the pipeline.
  • Preset chips: apply, plus a two-step delete on user presets (built-ins have no
    delete affordance; the backend also rejects it with 403).
  • Interactive Depth Shift viewer (pointer-driven parallax, intensity slider).
  • Multi-frame stacking mode: frame upload list, settings panel (alignment,
    combination, cosmic-ray/background toggles), step-by-step progress over the
    WebSocket, results panel with statistics, and "enhance composite" handoff.
  • Webhook token manager (create with one-time secret display, revoke).
  • AstroDex context detection from URL parameters and "send to AstroDex" action.
  • snake_case <-> camelCase conversion isolated to the API/WS clients.

Tooling and infra

  • scripts/check_deps_fresh.py: fails when any pin falls behind PyPI/npm; run in
    pytest and a weekly CI job.
  • Docker Compose (api, worker, web, redis) and a hot-reload
    docker-compose.dev.yml.
  • Playwright end-to-end suite (npm run test:e2e): upload -> slider -> process
    -> download, save-as-preset, and the full three-frame stacking flow with the
    "enhance composite" handoff. Boots the real backend and Vite dev server.
  • GitHub Actions: backend (ruff, mypy, pytest), frontend (lint, typecheck, test,
    build), and e2e (Playwright) jobs, plus the dependency-freshness cron.
  • API listens on port 8002.
  • Initial Alembic revision (606a1e113989_create_core_tables) covering all six
    tables; alembic upgrade head / downgrade base and alembic check verified
    clean against the ORM models.

API rate limiting

  • Per-IP request-rate limit (default 120/min, in-memory fixed window) on
    /upload, /process/{id}, /presets/{id}/apply/{session_id}, and
    /stack/*; over the limit returns 429 RATE_LIMITED. (Shipped at the API
    spec's original 10/min first; raised the same day after it broke e2e and
    would have broken real interactive editing - the editor re-processes on
    every debounced slider change.)
  • Per-IP concurrent-job limit (default 5), checked against the shared jobs
    table so it holds under both sync and queue processing modes.
  • rate_limit_enabled / rate_limit_per_minute / max_concurrent_jobs_per_ip
    in Settings -> Advanced; JobRecord.client_ip (new Alembic revision
    623faa14df02_add_job_client_ip).

Session-cleanup scheduler

  • worker runs Celery beat embedded (-B, both compose files): hourly,
    task_cleanup_sessions deletes sessions past session_expiry_hours and
    their files, regardless of PROCESSING_MODE. Schedule state persisted at
    DATA_DIR/celerybeat-schedule.

Performance benchmark suite

  • backend/tests/benchmarks/: codifies the Success Metrics acceptance
    criteria as runnable checks - full-res (~24MP) enhance (measured 3.0s vs a
    5s budget), preview (512px) reprocess (38ms vs 500ms), and an empirical
    stacking-SNR check (16 synthetic frames with known noise through
    combine(..., "mean"), measured 3.99x vs a theoretical 4.00x). Opt-in
    (RUN_BENCHMARKS=1 pytest tests/benchmarks --no-cov) rather than part of
    the default suite, since wall-clock budgets are sensitive to the machine and
    to coverage instrumentation; runs weekly via
    .github/workflows/benchmarks.yml, not on every push/PR.

Release mechanics

  • Root VERSION file is now the single source of truth for the app version
    (previously hardcoded separately in the backend and frontend); read via a
    build-time APP_VERSION / VITE_APP_VERSION Docker build argument, or
    straight from the file for local dev.
  • Single Docker image: the root Dockerfile (multi-stage: Node builds
    the frontend, Python runs the API) replaces the previous two-image split
    (backend/Dockerfile + frontend/Dockerfile behind nginx). FastAPI now
    serves the built React SPA directly (StaticFiles, mounted after every
    /api//ws route - the frontend's hash-based routing needs no
    SPA-fallback handling), with GZipMiddleware and the security response
    headers nginx used to add (X-Frame-Options, X-Content-Type-Options,
    Referrer-Policy) moved into app/main.py. A target: backend build
    (skips the frontend stage) serves worker and the dev api service, which
    never need the built UI. One less container, one URL (:8002) for
    everything instead of :8002 (API) + :3000 (nginx).
  • .github/workflows/release.yml: tag-triggered (v*.*.*) multi-arch build
    and push of the one image to both ghcr.io/myastroboard/myastroshine
    and Docker Hub's myastroboard/myastroshine, a Trivy scan, and a GitHub
    Release generated from this changelog.
  • .github/workflows/post-release-cleanup.yml: opens a PR that files this
    section under a dated release heading after a successful publish
    (scripts/changelog_release.py).
  • CODE_OF_CONDUCT.md, SECURITY.md.
  • docker-compose.yml gained image: alongside build: on api so a clean
    machine can docker compose up from the published image without cloning
    the repo; see docs/DEPLOYMENT.md.

Changed

Configuration moved out of the environment (PASSATION alignment, part 1)

  • docker compose up now needs no .env editing. The compose files carry only
    structural variables (APP_ENV, DATA_DIR, PROCESSING_MODE, the Redis URLs);
    docker-compose.yml dropped from 11 backend variables to 6 and holds no
    secrets.
  • Single persistence root DATA_DIR (/data in the image). The database,
    session images, stacking frames, cache, log file, secret_key.txt and
    app_settings.json all derive from it; the two data volumes were merged into
    one (myastroshine_data).
  • The session / HMAC-fallback secret is generated once into
    DATA_DIR/secret_key.txt (secrets.token_hex(32)) and never regenerated.
    ASTRODEX_WEBHOOK_SECRET is gone.
  • New app_settings.json holds every runtime-tunable value (CORS origins,
    AstroDex callback allowlist and retry policy, upload limit, session lifetime,
    preview size, ML denoise, depth method, stacking defaults, log levels...
Read more