Skip to content

Latest commit

 

History

70 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Storyboard Platform

A collaborative storyboard: planners, artists, and a director open the same board at the same time, break a script into cuts, generate the art, mark up the drawings with feedback, and sign off.

Runs on AWS. The drawings are generated by an open-weight model on our own GPU, not by a managed AWS model.

  • Try it: Run locally needs no AWS account. Deploy is one cdk deploy plus one script for the accounts.
  • The accounts that script creates: u1 Kim Hana (planner) · u2 Lee Dohyun (artist) · u3 Park Seojun (director) · u4 Choi Yujin (reviewer) · u5 Jung Mina (admin). You choose the password at deploy time · it is never stored in this repository.
  • Screens: app-walkthrough/screens/

What it does

Area Capability
Concurrent editing Many people edit one board. Merging is per field, so nobody overwrites your sentence. You see other people's cursors, avatars, and "editing now" markers
Planning Write the story from one prompt (Amazon Bedrock, Claude Sonnet): it invents the characters, the synopsis and the cut-by-cut flow. Options: new story / next episode / spin-off, genre, tone, runtime, cut count, how many characters to invent, whether to reuse the board's cast. You read the outline first, then it lands on the board. Or paste a script you already have and split it into cuts mechanically
Episodes One board holds the main story plus its next episodes and spin-offs. Cuts belong to an episode; characters are shared across all of them
Characters One character, many poses (front, 3/4, side, back, full body, expression). Pick one as the reference and the other poses follow that face
Art Generate from a prompt, upload a hand sketch, or transform an existing drawing. Pick one of three open-weight models per shot. Versions stack up, can be compared side by side, and can be deleted
Roles Five: planner, artist, director, reviewer, admin. Making art · generate, sketch upload, model swap · is for artists and planners. Story planning is for planners and directors. Approval is the director's, and so is the 관리 tab (assignment table, team load). Changing someone's role is the admin's. Three of those are enforced on the server (generation, planning, role changes); everything else is UI only (see the disclaimer)
Review Pin notes and circle marks on the drawing, tags (framing, light, background, wardrobe, expression, proportion), @mentions, replies, resolve
Approval Draft → in progress → in review → approved / changes requested. Buttons are locked for roles that don't own the action
Notifications A director's change request notifies the assignee and hands the cut back to them
Output Timeline (duration per scene), print / PDF

How it works

Browser ──┬─ CloudFront ─ S3               static files, no build step
          ├─ Cognito                       login; name and role come from token claims
          ├─ AppSync ─ DynamoDB            op log + realtime subscription (WebSocket)
          ├─ AppSync ─ Lambda ─ Bedrock    story planning · async job, result polled from DynamoDB
          └─ CloudFront /gen ─ ALB ─ EC2 g6e (L40S) ─ open-weight model ─ S3

Tech: plain ES modules in the browser (no framework, no bundler) · Amazon Cognito user pool (HTTP calls, no SDK) · AWS AppSync GraphQL with JS resolvers · DynamoDB as an append-only log · Amazon Bedrock (Claude Sonnet, called from a Lambda as an async job · see below) · CloudFront + S3 · one EC2 g6e.2xlarge running FastAPI + PyTorch + 🤗 diffusers · AWS CDK (JavaScript) for all of it.

Story planning is two calls, not one. The outline (title, logline, synopsis, characters, beats) comes back first and a person reads it; only then are the beats expanded into cuts, four beats per call, in parallel. One call for a 24-cut board would sit near the resolver's time limit and lose everything if it broke. The prompts live in the browser (app/story/story.js) so changing the wording isn't a resolver redeploy, and the model's JSON is clipped and whitelisted by normalizePlan() before any of it becomes an op · the model is a second trust boundary, not a trusted author.

Planning is an async job, because AppSync stops at 30 seconds. The AppSync quota "request execution time" is 30 seconds and is not adjustable, so a slow model cannot be awaited inside the request. The plan mutation invokes the Lambda with invocationType: 'Event' and returns a jobId immediately; the Lambda calls Bedrock, then writes {status, text, usage, stop} into the same DynamoDB table under PLAN#<jobId> with a one-hour TTL; the browser polls planResult(jobId) every 1.5 s for up to 120 s. net.plan() hides all of it, so story.js still just awaits a {text, usage, stop}. Only the account that started the job can read its result, and the Lambda writes a result on failure too · otherwise the browser would wait out the full 120 s for nothing.

Several models, one GPU. Pick per shot. Chroma and klein are Apache-2.0; Krea 2 Turbo and SD 3.5 are gated repos with their own licenses, so the Hugging Face key's account has to accept them before the box can download the weights.

Model Good for Reference image
krea Krea 2 Turbo the default: 8 steps, realistic, text only none · a shot with references is routed to klein
chroma Chroma1-Flash 8.9B 12 steps, ~15 s, the best pencil-storyboard texture img2img · keeps the layout, redraws the face
klein FLUX.2 klein 4B same character in a new shot; 8 steps, ~4 s condition · keeps the face, takes several images
klein9 FLUX.2 klein 9B (gated) the bigger klein: stills and reference shots when its weights are on disk (/healthref) condition · same pipeline, several images
hd Chroma1-HD 8.9B Flash before distillation: 26 steps, for a final pass img2img · keeps the layout, redraws the face

The reference-image column is the reason there are several. chroma/hd paint over the image with noise, so the layout survives and the face does not. klein takes it as a condition, so the face survives · that's what makes "one character, many shots" work. krea takes no image at all, so the board sends any shot that has a reference to klein instead of silently drawing from text.

Assets (assets.html). A fifth tab, after the storyboard. Characters, locations and props live here in three categories, plus the stills made from them. Three ways in: when a director approves a cut, the approver's browser asks klein to redraw the approved image three ways · each cast member alone on a plain background, the location with every person removed, the key prop by itself (server.py's ISOLATE); hand-drawn or photographed references uploaded from the page; and pictures made from a prompt per category. Prompted pictures come out as candidates (1, 2 or 4 per prompt, photoreal) and only the ones you keep become assets · everything kept is an asset.* op on the same log the board reads. Pick several assets ("a swim cap", "a happy family", "a wide clear sea") and the page makes photoreal stills in 1, 2 or 4 versions (1920×1088, style: real): the references go to klein as a list, so the same faces, place and prop come back in one new composition with a film look instead of pencil. Versions land in the same candidate tray; a viewer opens them at 1:1 and 2× with drag-pan so you can compare before keeping. Candidates and the queue of pending versions live in the browser (localStorage), so a planner can walk over to the storyboard and come back to find the work still there; a request cut off mid-flight is recovered from the GPU server, which keeps each job's result for two hours (/gen/result/{job}). Uploaded references go to S3 through the connector Lambda (putImage) and stay until deleted, like everything else you keep. The storyboard offers the kept assets as reference assets when generating a cut; a cut's cast and its scene's background are preselected, props are picked by hand.

Only one of them fits in the card's 48 GB at a time, so picking a model swaps it: the server loads the new weights in the background and refuses generation until they're resident (~1 min from disk). The picker shows the wait. krea is what a fresh deploy starts with (MODEL in the stack).

GPU power. The box used to be switched off by a schedule at 20:00 KST; that left demos stranded in the evening. Now only the morning start (09:00) is scheduled and anyone but a reviewer can press GPU 끄기 / GPU 켜기 in the storyboard's model menu or on the assets page (gpuPower, via the connector Lambda and EC2). A running g6e.2xlarge costs about $2 an hour, so switch it off when the day is done.

The only truth on the board is the op log. One edit is one immutable op; on boot the client replays the log to build the screen. There is no separate stored state, which is why two people's edits can always be merged.

  • Ordering uses fractional indexing, so simultaneous reorders don't corrupt the sequence.
  • Same field: last write wins, per field. Different fields: both survive.
  • Ops from other people must pass scrub() in app/story/core.js before they touch state. That is the trust boundary.

Code structure

Three directories, and that is the whole repository: app/ is the product, app-walkthrough/ is everything that exists only for the guided example, infra/ is the AWS side. Anyone changing a feature reads app/ and infra/ and can ignore app-walkthrough/ entirely.

Inside app/, files are grouped by who reads them. platform/ has no product opinion, chrome/ is what every screen shares, story/ is the state rules and the ontology graph, and screens/ is one file per screen. The four .html files stay at the top because that is where their URLs are.

app/                 browser. no framework, no build. this directory becomes the bucket root
  index.html           home · the four steps in story order, and the login gate
  board.html           storyboard screen (markup and all CSS)
  story-graph.html     story develop + script-to-graph screen (markup and CSS)
  key-visual.html      key visual: script → one image per scene
  screens/             one file per screen. each one imports from the folders below
    home.js              home: the step list, the project cards, the example button
    board.js             board, detail, viewer, markup, timeline, print
    story-graph.js       script → graph → seeds → branches → cuts → script
    key-visual.js        script → scenes → prompts → one image per scene
  platform/            no product opinion. swap any of these and the screens do not notice
    net.js               transport. AppSync when configured, BroadcastChannel between tabs when not
    auth.js              Cognito login and token refresh · two HTTP calls, no SDK
    login.js             login screen
    dom.js               the only place that writes HTML into the DOM, plus escaping
  chrome/              what every screen shares
    nav-tabs.js          the top tab bar, plus NAV_TABS (the step order)
    theme.css            the one set of colours and fonts (--sb-*) all four screens read
    history.js           "what happened" list · who did what, and continue-from-here
    projects.js          the project picker and the cards on home
    coach.js             coachmarks · the veil, the bubble, the keyboard handling
    empty-panel.js       the "처음 오셨나요?" panel every screen shows while empty
  story/               the rules. no DOM in here
    core.js              status transitions, role permissions, field merge, op validation,
                         plan normalization. pure functions, testable without a browser
    story.js             story planning: the option set, the prompts, and the two-phase Bedrock
                         call. falls back to splitting the prompt when there is no model
    graph-*.js           the ontology graph: schema, layout engine, canvas view, probe queries
  art/
    art.js               canvas-drawn placeholder art for local mode
    seed-art.js          generated by infra/scripts/seed-art.mjs; the starter board's images
  aws-config.js        `null` in the repo (that's what selects local mode); CDK overwrites it at deploy
  test.html            self-check · open it in a browser and it prints PASS/FAIL. not deployed
app-walkthrough/     example only. nothing under app/ is imported from here, and cannot be:
                     deploy puts app/* at the bucket root and this folder under its own prefix, so
                     a path from here back into app/ resolves on disk and 404s in production. The
                     screens inject what the example needs instead (tour.js `wire`). Delete this
                     folder and the example goes away; the product still runs.
  tour.js              the one walkthrough across screens · ?demo=1, which screen is next
  guide.js             the guide engine: the veil with a hole, the bubble, wait/done, the note lane
  steps/               what each screen's example actually does, step by step
    develop.js           synopsis → graph → seeds → one branch
    key-visual.js        the sample script, its prompts, and browser-drawn stand-in art
    board.js             replays the seed ops one stage at a time, not all at once
  data/                the fixtures the example replays · graph, seeds, stories, a synopsis
  data/build.mjs       rebuilds extracted-graph.json from the raw model response
  screens/             screenshots and the key-visual mockup. not deployed
infra/
  bin/app.js           CDK entry point
  lib/storyboard-stack.js   the whole stack, one file
  schema.graphql       Op, Presence and PlanJob types, the queries/mutations, two subscriptions
  resolvers/           AppSync JS resolvers · putOp, listOps, presence, plan (async job) + planResult,
                       and the four graph resolvers
  graph/index.js       the one Lambda: Neptune Gremlin queries + the Bedrock plan job
  gpu/server.py        FastAPI generation server: the three-model registry, one-resident-at-a-time
                       VRAM swapping, prompt assembly per model family, upload to S3
  gpu/user-data.sh     GPU boot script · pinned Python deps, systemd unit, model pre-download
  scripts/users.sh     create the five demo accounts
  scripts/seed-art.mjs generate the starter board's images with the real model
  scripts/stop.sh      stop the Neptune cluster (it bills by the hour while it runs)
  scripts/start.sh     start it again · same endpoint, same data

Run locally

node infra/scripts/serve-local.mjs
# http://localhost:8000 · no login, and multiple tabs sync with each other live

Use that script rather than python3 -m http.server: the deployed layout is not the repository layout. CDK uploads app/* to the bucket root and app-walkthrough/* to a folder of that name, so serving the repository directly gives 404 for / and /board.html. serve-local.mjs reproduces the deployed paths, and it returns 403 for directory URLs exactly like CloudFront does · otherwise a broken link works locally and only breaks in production.

With an empty app/aws-config.js the app runs in local mode (BroadcastChannel + localStorage) and draws placeholder art instead of calling a GPU. Good enough to see concurrent editing work.

app/art/seed-art.js ships empty, so the starter board draws canvas placeholders · the same locally and on a fresh deployment. scripts/seed-art.mjs fills it with real generated art once a GPU is up, but those /img/<hash>.png paths only resolve in the account that generated them. That is why the file is committed empty: any account gets the same board on the first boot.

Deploy

Two commands: cdk deploy builds everything, scripts/users.sh creates the accounts. Four things have to be true before the first one · none of them are things CDK can do for you.

  1. Region ap-northeast-2. Not a preference. The stack is pinned there in four places: the CloudFront origin-facing prefix list ID (pl-22a6434b · different in every region), the g6e AZ list (only 2a and 2b sell it here), the global.anthropic.claude-sonnet-4-6 inference profile in infra/resolvers/plan.js, and infra/scripts/seed-art.mjs. Elsewhere means editing all four. Two cautions learned the hard way. Model prefix: us.* profiles exist only in US regions; outside them use global.*, or the plan resolver returns a Bedrock error. Capacity vs. availability: run-instances --dry-run does not verify either. It reports "would have succeeded" for a type the region does not even sell, so use aws ec2 describe-instance-types --region <r> --instance-types g6e.2xlarge to check that a region offers the card at all, and expect capacity itself to be provable only by launching. This was us-east-1, then us-west-2; g6e.2xlarge ran dry in every AZ of both. ap-southeast-1 is not a fallback · it has no g6e at all (T4/T4g/A100 only).
  2. A default VPC in that region. The stack looks one up rather than creating one.
  3. Bedrock model access for Anthropic Claude, enabled once per account on the Bedrock console's Model access page. Story planning returns AccessDeniedException until it is. Check with aws bedrock get-foundation-model-availability --region ap-northeast-2 --model-id anthropic.claude-sonnet-4-6 · you want "authorizationStatus": "AUTHORIZED".
  4. At least 8 G-instance vCPUs. g6e.2xlarge needs 8 under Running On-Demand G and VT instances (L-DB2E81BA); a new account can start at 0. Without it the instance fails to launch and the stack rolls back. Request the increase first.

Then npx cdk bootstrap once per account and region. cdk.context.json is not committed, so the VPC lookup runs again on your first synth.

cd infra && npm ci
npx cdk deploy --outputs-file /tmp/sb-out.json    # prints Url, UserPoolId, ClientId, GpuInstance

POOL=$(node -p "require('/tmp/sb-out.json').StoryboardDemo.UserPoolId")
SB_PW='<pick a password>' bash scripts/users.sh "$POOL"  # the five demo accounts
SB_PW='<the same password>' node scripts/seed-art.mjs    # optional: real art for the starter board
npx cdk deploy                                           # only if you ran seed-art: it writes a local file

The password only ever arrives through SB_PW, never as an argument and never in a file · arguments show up in shell history and ps. Leave SB_DEMO_PW unset: it prefills the login form for a private demo, and setting it writes the password into aws-config.js, which anyone can read over CloudFront.

The GPU is only warm after it has pulled the model (tens of GB). When the generation-server chip in the header changes from 모델 올리는 중 to the model name, it's ready. First boot takes a few minutes.

Working hours, not idle detection

The GPU is one EC2 instance, and two EventBridge Scheduler rules stop and start it · weekdays 09:00–20:00 KST (GPU_HOURS in infra/lib/storyboard-stack.js, written in UTC). Outside those hours the instance is stopped and the hourly charge is not running. About $370/month instead of $1,600.

Say what this is precisely: it is a schedule, not idle detection. The GPU is on at 09:00 whether anyone is using it or not, and it goes down at 20:00 whether they are or not. Nothing wakes it up when a request arrives · outside the window, generation fails until someone starts it.

Editing GPU_HOURS is the whole configuration. The rules call ec2:StartInstances / ec2:StopInstances directly through Scheduler's universal target, so there is no Lambda to read.

The root volume survives a stop. deleteOnTermination: false, so the ~67 GB of model weights on /opt/hf are still there in the morning and nothing is re-downloaded · that is the only reason a stop/start schedule is worth having here. Two consequences:

  • The instance is a single ec2.Instance, not an Auto Scaling group. An ASG terminates on scale-in, so a retained volume would be left attached to nothing, billed monthly, and unusable by whatever launched next.
  • cdk destroy does not delete the volume. Delete it yourself afterwards or it keeps costing ~$27/month:
    aws ec2 describe-volumes --filters Name=status,Values=available \
      --query "Volumes[?Size==\`200\`].[VolumeId,CreateTime]" --output table
    aws ec2 delete-volume --volume-id vol-…

On and off by hand, when the schedule isn't what you want:

GPU=$(aws cloudformation describe-stacks --stack-name StoryboardDemo \
  --query "Stacks[0].Outputs[?OutputKey=='GpuInstance'].OutputValue" --output text)
aws ec2 stop-instances  --instance-ids "$GPU"   # off
aws ec2 start-instances --instance-ids "$GPU"   # on

A manual start does not disable the schedule: the next 20:00 rule still stops it. To keep it up overnight, disable the GpuOff schedule as well.

Stop the graph database too. Neptune runs on a provisioned instance (db.t4g.medium by default), so it bills by the hour whether or not anyone queries it. Stopping keeps the data and the endpoint: start.sh brings back the same cluster, and neither the graph Lambda nor the AppSync resolvers need redeploying.

bash scripts/stop.sh    # stop · takes a few minutes
bash scripts/start.sh   # start · the graph Lambda errors until the status is `available`

Both default to cluster storyboarddemo-graph in ap-northeast-2; override with SB_NEPTUNE_CLUSTER and AWS_REGION if you deployed the stack under another name or region.

Neptune restarts a stopped cluster by itself after 7 days. That is an AWS limit, not something the scripts can opt out of. If you are done with the demo for more than a week, either re-run stop.sh each week or npx cdk destroy · otherwise the cluster quietly comes back and bills.

A bigger instance takes no code change:

npx cdk deploy --context neptuneInstance=db.r6g.large

Tear everything down: npx cdk destroy · then delete the volume above.

Using it

Open the site in two windows and log in as different people · a new tab starts a fresh session, so u2 in one window and u3 in the other. Both are looking at the same board.

Example: prompt → story → art → review → approval

  1. Plan (u1). Press + 이야기 기획 in the sidebar. Type what the film is about, pick genre, tone, runtime, cut count and how many characters to invent, then press 개요 만들기 (~10 s). Read the outline · title, logline, synopsis, the characters it invented, the beat list · and press 이대로 컷 만들기. Characters, their pose sheets and the cuts all land on the board at once. Assign a cut to u2. With a story already on the board, the same dialog offers 다음 회차 and 스핀오프: those land as a new episode in the sidebar and leave the existing cuts alone. (Already have a script? 시나리오 → 컷으로 분해 still splits it mechanically, no model involved.)
  2. Character (u2). Open 인물, add a character, write a short physical description, generate the pose sheet. Mark one pose as the reference · later cuts with that character start from it.
  3. Art (u2). Open the cut, edit 작업 지시, press 생성. ~15 s. Or upload a hand sketch and press 생성 with 구도 유지 to ink it. Every attempt becomes a version; the version strip compares them.
  4. Review (u3). Open the drawing, click on it to drop a pin, drag to circle, tag it 조명, and press 수정 요청 with a memo. u2 gets a notification and the cut comes back to them.
  5. Fix and approve. u2 regenerates, presses 리뷰 요청, u3 presses 승인. The cut locks; nobody can edit approved content until it's reopened.
  6. Ship. Open 타임라인 for per-scene durations, or 인쇄 for a PDF.

While you do this, watch the other window: everything lands there within a second, without a refresh.

Cost

Item Roughly
EC2 g6e.2xlarge (L40S 48GB) $2.24/hour · ~$54 for a full day (us-east-1 on-demand)
EBS 200GB gp3 @ 500 MB/s ~$27/month (the extra throughput is what makes a model swap ~1 min instead of ~4)
ALB ~$0.025/hour + traffic
Neptune db.t4g.medium ~$0.09/hour while running · $0 stopped, storage aside (scripts/stop.sh)
CloudFront · S3 · Cognito · AppSync · DynamoDB a few dollars at demo scale

GPU uptime is essentially the entire bill. Nothing shuts it down for you, and nothing shuts Neptune down either · and Neptune wakes itself back up 7 days after you stop it. | EC2 g6e.2xlarge (L40S 48GB) | $2.24/hour · ~$370/month on the default 09–20 weekday schedule · over $1,600 if it never stops | | EBS 200GB gp3 @ 500 MB/s | ~$27/month, including the stopped hours · it is retained on purpose, and cdk destroy leaves it behind | | ALB | ~$0.025/hour + traffic, ~$19/month · it does not stop with the GPU | | CloudFront · S3 · Cognito · AppSync · DynamoDB | a few dollars at demo scale |

GPU uptime is essentially the entire bill, and the schedule is what bounds it. Everything else keeps running while the GPU is stopped: the ALB, the retained volume, and the board itself.

Known limits

  • Pins and circle marks don't appear in print/PDF · they're screen-only.
  • The op log has a 30-day TTL. The log is the board, so edits older than 30 days disappear. If you want to keep a board, remove the TTL first.
  • Deleting an image version hides it from the board · the log records a tombstone, so v-numbers never shift and older notes keep pointing at the right drawing. The S3 object stays: the images bucket has no lifecycle rule and /img/* is cached for a year.
  • Generation is one image at a time (one GPU, one server). Simultaneous requests queue.
  • One model is resident at a time. Two people who want different models take turns, and each switch costs a load. If that becomes the bottleneck, the fix is a queue plus one worker per model.
  • Edits made while your browser was closed are backfilled from the log on reopen. But your own ops that failed to send are retried four times and then dropped silently.
  • Story planning is the one call with no retry and no outbox. A paid call must not be re-sent behind your back, so a failure is shown and you press the button again. The Lambda's async retries are off (retryAttempts: 0) for the same reason.
  • A plan job outlives the page. Close the tab mid-plan and the Lambda still finishes and pays for the tokens; nothing reads the result and the TTL sweeps it an hour later.
  • A landed plan has no undo. Once you press 이대로 컷 만들기, the cuts and characters are ops on everyone's board · delete them one by one. Reading the outline first is the safety net.
  • Nothing rate-limits planning. Any planner or director can press the button as often as they like.
  • Local mode has no model. The plan dialog still works, but it splits your prompt into a skeleton and says so · the real thing needs the deployed board.
  • The first person to open an empty board seeds the starter board from the browser. A real deployment should move that seeding to deploy time.

Disclaimer

This is a demo built to show a concept. It is not a product and is not in a state to put into production. Read all of the below before you deploy it yourself. The author takes no responsibility for damages, costs, or leaks resulting from using it as-is. This is not an official AWS product or sample and has no affiliation with AWS.

Cost · the GPU is $2.24/hour while it runs. The default schedule holds that to weekday 09:00–20:00 KST, about $370/month, but it is a fixed schedule and not a safety net: nothing notices that nobody is using the instance, and a manual start stays up until the next 20:00 rule. The ALB and the retained 200 GB volume are billed around the clock, and the volume outlives cdk destroy. There is no budget alarm. Deploying starts the meter.

Security · fix these before deploying to an organization

The demo prioritized showing the idea, and the price was leaving the following open. It assumes a demo environment that only trusted people can reach.

  1. The server does not verify the author of an op. Any logged-in user can record an edit or approval under someone else's name.
  2. Roles cannot be self-assigned · the app client's write list is the display name only, so custom:role changes take an admin API call. The display name is self-writable, though: a name in the roster is not proof of who someone is.
  3. Role checks for approve, delete (cuts and image versions), and board reset exist only in the UI. The server does not enforce them. (Three exceptions check the token claim server-side: generation on the GPU server, roster role changes in putOp.js, and story planning in plan.js.)
  4. The five demo accounts share one password and it never expires. Reissue per account.
  5. The CloudFront → GPU leg is plain HTTP (user → CloudFront is HTTPS). Attach a domain and terminate TLS, or use a VPC origin.
  6. AppSync logging, CloudFront access logs, and DynamoDB point-in-time recovery are all off. There is no record to trace an incident with.
  7. The generation server runs as root and there is no upper bound on the step count in a request. A logged-in user can hold the GPU for a long time.
  8. Deploying with SB_DEMO_PW set writes the password into a public file (aws-config.js). Never set it for a public demo.
  9. Story planning has no rate limit and no spend cap. The resolver checks the role and clips the token count per call, but a planner or director can call Bedrock in a loop. Add a per-user quota, or an AWS Budgets alarm, before letting a wider group in.

Image generation · art is produced by open-weight models (Krea 2 Turbo, Chroma1-Flash, FLUX.2 klein 4B, Chroma1-HD, SD 3.5 Large) running on our own EC2 instance. You are responsible for checking the license of each model and its weights and what you may do with the output. Bedrock writes the image prompts in English, so they reach the GPU untouched. A prompt line someone typed Korean into by hand is translated on the way in, and that call goes to Bedrock too (en() in infra/gpu/server.py, which skips any ASCII string): every model call in the project, planning and translation alike, is a Bedrock call. Output may not match the prompt, and the same seed produces different images when the model or library versions change.

Data · board contents, uploaded sketches, and generated images are stored in this account's S3 and DynamoDB. Encryption is S3 default (AES256) only, with no separate KMS key. Do not put real production material or personal data in it. cdk destroy deletes the buckets and the table with it, and that is irreversible.

Characters · the starter board's script, characters, and names (Yeorum Studio, Kim Hana, Lee Dohyun, Park Seojun, Choi Yujin) are fictional and exist only for the demo.


Security

See CONTRIBUTING for more information.

License

This library is licensed under the MIT-0 License. See the LICENSE file.

About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages